diff --git a/cmd/main.go b/cmd/main.go index 5a4e9ee..19202f6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "github.com/codegangsta/martini" "github.com/codegangsta/martini-contrib/binding" "github.com/codegangsta/martini-contrib/render" @@ -25,7 +26,7 @@ func main() { m := martini.Classic() app, err := application.New("application.conf", "APP") if err != nil { - log.Fatalf("cn't configure app") + log.Fatal(fmt.Errorf("can't build app: %w", err).Error()) } m.Map(log.New(os.Stdout, "[app]", log.Lshortfile)) @@ -33,7 +34,7 @@ func main() { m.Use(sessions.Sessions("app", sessions.NewCookieStore([]byte("BfyfgIyngIOUgmOIUgt87thrg5RHn78b")))) m.Use(auth.SessionUser(auth.GenerateAnonymousUser)) m.Use(render.Renderer(render.Options{ - Directory: "templates", + Directory: "templates", Extensions: []string{".tmpl"}, })) @@ -57,6 +58,9 @@ func main() { m.Get("/signup", handlers.GetSignup) m.Post("/signup", binding.Bind(auth.UserModel{}), handlers.PostSignup) + m.Get("/subscribe", handlers.GetSubscribe) + m.Get("/unsubscribe", handlers.GetUnSubscribe) + //Анкета текущего пользователя m.Get("/", auth.LoginRequired, handlers.GetHome) diff --git a/go.mod b/go.mod index 3a2d553..2399027 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,11 @@ module github.com/tiburon-777/OTUS_HighLoad go 1.15 require ( - github.com/BurntSushi/toml v0.3.1 github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect github.com/codegangsta/martini v0.0.0-20170121215854-22fa46961aab github.com/codegangsta/martini-contrib v0.0.0-20140208234550-8ce6181c2609 - github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab // indirect github.com/go-sql-driver/mysql v1.5.0 github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect - github.com/martini-contrib/cors v0.0.0-20141016003011-553b9208d353 - github.com/stretchr/testify v1.6.1 github.com/tiburon-777/modules v0.0.0-20201210103219-a0362a8da783 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index cecdf1b..ef79f99 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/codegangsta/martini-contrib v0.0.0-20140208234550-8ce6181c2609/go.mod github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -25,8 +23,6 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/martini-contrib/cors v0.0.0-20141016003011-553b9208d353 h1:kXDXsKbuZYwJXBVeLT49PwPvAfQg74/8mOZUqrfV3cg= -github.com/martini-contrib/cors v0.0.0-20141016003011-553b9208d353/go.mod h1:ZQqqR+M04limsCsRirtK4IAN2ioLKKNxhvl7f0U40yw= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/application/application.go b/internal/application/application.go index a6ecf3b..e13e108 100755 --- a/internal/application/application.go +++ b/internal/application/application.go @@ -5,41 +5,70 @@ import ( "fmt" "github.com/tiburon-777/OTUS_HighLoad/internal/models" "github.com/tiburon-777/modules/core/config" + "log" ) type App struct { Config *models.Configuration - DB *sql.DB + DB *sql.DB } func New(configFile, envPrefix string) (App, error) { conf, err := configure(configFile, envPrefix) - if err != nil{ - return App{}, fmt.Errorf("can't apply config: %w\n",err) + if err != nil { + return App{}, fmt.Errorf("can't apply config: %w\n", err) } db, err := sql.Open("mysql", conf.DSN.User+":"+conf.DSN.Pass+"@tcp("+conf.DSN.Host+":"+conf.DSN.Port+")/"+conf.DSN.Base) if err != nil { - panic(err.Error()) + return App{}, err + } + if err = dbInit(db); err != nil { + return App{}, err } - return App{Config: conf, DB: db}, nil } -func configure(fileName string, envPrefix string) (*models.Configuration,error) { +func configure(fileName string, envPrefix string) (*models.Configuration, error) { var conf models.Configuration s := config.New(&conf) if fileName != "" { - fmt.Printf("try to apply config from file %s...\n", fileName) + log.Printf("try to apply config from file %s...\n", fileName) if err := s.SetFromFile(fileName); err != nil { return &models.Configuration{}, fmt.Errorf("can't apply config from file: %w", err) } } if envPrefix != "" { - fmt.Printf("try to apply config from environment...\n") + log.Println("try to apply config from environment...") if err := s.SetFromEnv(envPrefix); err != nil { return &models.Configuration{}, fmt.Errorf("can't apply envvars to config:%w", err) } } return &conf, nil -} \ No newline at end of file +} + +func dbInit(db *sql.DB) error { + log.Println("Check DB tables consistency...") + if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS users ( + Id int(11) NOT NULL AUTO_INCREMENT, + Username varchar(255) DEFAULT NULL, + Password varchar(255) DEFAULT NULL, + Name varchar(255) DEFAULT NULL, + Surname varchar(255) DEFAULT NULL, + BirthDate datetime DEFAULT NULL, + Gender varchar(255) DEFAULT NULL, + City varchar(255) DEFAULT NULL, + Interests varchar(255) DEFAULT NULL, + PRIMARY KEY (Id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8`); err != nil { + return err + } + if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS relations ( + userId int(11) DEFAULT NULL, + friendId int(11) DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8`); err != nil { + return err + } + log.Println("All tables exists") + return nil +} diff --git a/internal/auth/login.go b/internal/auth/login.go index 71bbafb..87b3793 100644 --- a/internal/auth/login.go +++ b/internal/auth/login.go @@ -2,12 +2,12 @@ package auth import ( "fmt" -"github.com/codegangsta/martini" -"github.com/codegangsta/martini-contrib/render" -"github.com/codegangsta/martini-contrib/sessions" + "github.com/codegangsta/martini" + "github.com/codegangsta/martini-contrib/render" + "github.com/codegangsta/martini-contrib/sessions" "github.com/tiburon-777/OTUS_HighLoad/internal/application" "log" -"net/http" + "net/http" ) // These are the default configuration values for this package. They @@ -100,4 +100,3 @@ func UpdateUser(s sessions.Session, user User) error { s.Set(SessionKey, user.UniqueId()) return nil } - diff --git a/internal/auth/user.go b/internal/auth/user.go index 5ad9274..25ea3d0 100644 --- a/internal/auth/user.go +++ b/internal/auth/user.go @@ -7,18 +7,19 @@ import ( ) type UserModel struct { - Id int64 `db:"id" form:"id"` - Username string `db:"username" form:"username"` - Password string `db:"password" form:"password"` - Name string `db:"name" form:"name"` - Surname string `db:"surname" form:"surname"` - BirthDate time.Time `db:"birthdate"` - YearsOld int `db:"-" form:"-"` - FormBirthDate string `form:"birthdate"` - Gender string `db:"gender" form:"gender"` - City string `db:"city" form:"city"` - Interests string `db:"interests" form:"interests"` - authenticated bool `db:"-" form:"-"` + Id int64 `db:"id" form:"id"` + Username string `db:"username" form:"username"` + Password string `db:"password" form:"password"` + Name string `db:"name" form:"name"` + Surname string `db:"surname" form:"surname"` + BirthDate time.Time `db:"birthdate"` + YearsOld int `db:"-" form:"-"` + FormBirthDate string `form:"birthdate"` + Gender string `db:"gender" form:"gender"` + City string `db:"city" form:"city"` + Interests string `db:"interests" form:"interests"` + IsFriend bool `db:"-" form:"-"` + authenticated bool `db:"-" form:"-"` } func GenerateAnonymousUser() User { @@ -59,6 +60,6 @@ func (u *UserModel) GetById(app application.App, id interface{}) error { if err != nil { return err } - u.Id=id.(int64) + u.Id = id.(int64) return nil -} \ No newline at end of file +} diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index c1cdb4c..1a85643 100755 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -9,13 +9,50 @@ import ( "github.com/tiburon-777/OTUS_HighLoad/internal/auth" "log" "net/http" + "strconv" "time" ) -func GetHome(r render.Render, user auth.User) { +func GetHome(app application.App, r render.Render, user auth.User) { h := user.(*auth.UserModel).BirthDate - user.(*auth.UserModel).YearsOld = int(time.Since(h).Hours()/8760) - r.HTML(200, "index", user) + user.(*auth.UserModel).YearsOld = int(time.Since(h).Hours() / 8760) + doc := make(map[string]interface{}) + doc["user"] = user.(*auth.UserModel) + var users []auth.UserModel + var tmp auth.UserModel + var tmpTime string + query := fmt.Sprintf(`SELECT + users.id as id, + users.name as name, + users.surname as surname, + users.birthdate as birthdate, + users.gender as gender, + users.city as city + FROM + users JOIN relations + WHERE + relations.friendId=users.Id + AND relations.userId="%s" + GROUP BY users.Id`, + strconv.Itoa(int(user.(*auth.UserModel).Id)), + ) + var results, err = app.DB.Query(query) + if err != nil || results == nil { + err500("can't get user list from DB: ", err, r) + } + defer results.Close() + for results.Next() { + err = results.Scan(&tmp.Id, &tmp.Name, &tmp.Surname, &tmpTime, &tmp.Gender, &tmp.City) + if err != nil { + err500("can't scan result from DB: ", err, r) + } + tmp.BirthDate = str2Time(tmpTime, r) + tmp.YearsOld = int(time.Since(tmp.BirthDate).Hours() / 8760) + users = append(users, tmp) + } + doc["table"] = users + + r.HTML(200, "index", doc) } func GetSignup(r render.Render) { @@ -35,7 +72,7 @@ func PostSignup(app application.App, postedUser auth.UserModel, r render.Render) query := fmt.Sprintf(`INSERT INTO users (username, password, name, surname, birthdate, gender, city, interests) values ("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s")`, postedUser.Username, - base64.StdEncoding.EncodeToString([]byte(postedUser.Username + ":" + postedUser.Password)), + base64.StdEncoding.EncodeToString([]byte(postedUser.Username+":"+postedUser.Password)), postedUser.Name, postedUser.Surname, t.Format("2006-01-02 15:04:05"), @@ -52,35 +89,35 @@ func PostSignup(app application.App, postedUser auth.UserModel, r render.Render) func GetUserList(app application.App, user auth.User, r render.Render) { doc := make(map[string]interface{}) - doc["user"]=user.(*auth.UserModel) + doc["user"] = user.(*auth.UserModel) var users []auth.UserModel var tmp auth.UserModel var tmpTime string - var results, err = app.DB.Query(`SELECT name, surname, birthdate, gender, city FROM users`) - if err != nil || results==nil { + var results, err = app.DB.Query(`SELECT id, name, surname, birthdate, gender, city FROM users`) + if err != nil || results == nil { err500("can't get user list from DB: ", err, r) } - defer results.Close() + defer results.Close() for results.Next() { - err = results.Scan(&tmp.Name, &tmp.Surname, &tmpTime, &tmp.Gender, &tmp.City) + err = results.Scan(&tmp.Id, &tmp.Name, &tmp.Surname, &tmpTime, &tmp.Gender, &tmp.City) if err != nil { err500("can't scan result from DB: ", err, r) } tmp.BirthDate = str2Time(tmpTime, r) - tmp.YearsOld = int(time.Since(tmp.BirthDate).Hours()/8760) - users = append(users,tmp) + tmp.YearsOld = int(time.Since(tmp.BirthDate).Hours() / 8760) + users = append(users, tmp) } - doc["table"]=users + doc["table"] = users r.HTML(200, "list", doc) } func PostLogin(app application.App, session sessions.Session, postedUser auth.UserModel, r render.Render, req *http.Request) { - hash := base64.StdEncoding.EncodeToString([]byte(postedUser.Username + ":" + postedUser.Password)) + hash := base64.StdEncoding.EncodeToString([]byte(postedUser.Username + ":" + postedUser.Password)) user := auth.UserModel{} query := fmt.Sprintf("SELECT id FROM users WHERE username=\"%s\" and password =\"%s\"", postedUser.Username, hash) err := app.DB.QueryRow(query).Scan(&user.Id) - if err != nil || user.Id==0 { + if err != nil || user.Id == 0 { r.Redirect(auth.RedirectUrl) return } else { @@ -95,6 +132,47 @@ func PostLogin(app application.App, session sessions.Session, postedUser auth.Us } } +func GetSubscribe(app application.App, r render.Render, user auth.User, req *http.Request) { + sid, ok := req.URL.Query()["id"] + if !ok { + err500("can't parce URL query", nil, r) + } + did, err := strconv.Atoi(sid[0]) + if err != nil { + err500("can't convert URL query value: ", err, r) + } + query := fmt.Sprintf(`REPLACE INTO relations (userId, friendId) values ("%d", "%d")`, + user.(*auth.UserModel).Id, + did, + ) + _, err = app.DB.Exec(query) + if err != nil { + err500("can't create relation in DB: ", err, r) + } + r.Redirect("/list") +} + +func GetUnSubscribe(app application.App, r render.Render, user auth.User, req *http.Request) { + sid, ok := req.URL.Query()["id"] + if !ok { + err500("can't parce URL query", nil, r) + } + did, err := strconv.Atoi(sid[0]) + if err != nil { + err500("can't convert URL query value: ", err, r) + } + query := fmt.Sprintf(`DELETE FROM relations WHERE userId="%d" AND friendId="%d"`, + user.(*auth.UserModel).Id, + did, + ) + _, err = app.DB.Exec(query) + if err != nil { + err500("can't remove relation from DB: ", err, r) + } + r.Redirect("/") + +} + func str2Time(s string, r render.Render) time.Time { t, err := time.Parse("2006-01-02 15:04:05", s) if err != nil { diff --git a/internal/models/config.go b/internal/models/config.go index acf38e1..271b283 100755 --- a/internal/models/config.go +++ b/internal/models/config.go @@ -2,18 +2,18 @@ package models type Configuration struct { Server Server - DSN DSN + DSN DSN } type Server struct { - Address string - Port string + Address string + Port string } type DSN struct { - Host string - Port string - User string - Pass string - Base string -} \ No newline at end of file + Host string + Port string + User string + Pass string + Base string +} diff --git a/templates/index.tmpl b/templates/index.tmpl index f536666..75cc10e 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -1,14 +1,56 @@ -
-Hello {{ .Name }} {{ .Surname }}
-You gender is {{ .Gender }}
-You {{ .YearsOld }} years old
-You now live in {{ .City }}
-You interests is: {{ .Interests }}
+ + + + +Hello {{ .user.Name }} {{ .user.Surname }}
+You gender is {{ .user.Gender }}
+You {{ .user.YearsOld }} years old
+You now live in {{ .user.City }}
+You interests is: {{ .user.Interests }}
- -Name | +Years Old | +Gender | +City | +Action | +
---|---|---|---|---|
{{ $value.Name }} {{ $value.Surname }} | +{{ $value.YearsOld }} | +{{ $value.Gender }} | +{{ $value.City }} | +Unsubscribe | +