diff --git a/Gopkg.lock b/Gopkg.lock index 8f32577..df6664b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -25,6 +25,12 @@ revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" +[[projects]] + name = "github.com/pkg/errors" + packages = ["."] + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + [[projects]] name = "github.com/pmezard/go-difflib" packages = ["difflib"] @@ -72,6 +78,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "de251dc8079f7e77340889e2f857789870c28138c729203c2cf264065fc389c0" + inputs-digest = "7b9ffd41450c36b9333f821601a61803524c11caeffc23a5927e4e9bde8bbfbb" solver-name = "gps-cdcl" solver-version = 1 diff --git a/app/app.go b/app/app.go index 1e0db58..e5b5d37 100644 --- a/app/app.go +++ b/app/app.go @@ -1,6 +1,9 @@ package app import ( + "html/template" + "path/filepath" + "github.com/Diminho/MK_practice/mk_session" "github.com/Diminho/MK_practice/models" @@ -19,9 +22,23 @@ type App struct { SrvRootDir string AbsSrvRootDir string Manager *mk_session.Manager + Tmpl *template.Template } -func NewApp(slog *simplelog.Log, fbConfigOauth *oauth2.Config, FBState string, SrvRootDir string, instance func() models.Database) *App { +func NewApp(slog *simplelog.Log, fbConfigOauth *oauth2.Config, FBState string, SrvRootDir string, instance func() models.Database) (*App, error) { + + absSrvRootDir, err := filepath.Abs(SrvRootDir) + + if err != nil { + slog.Error(err) + } + + tmpl, err := ParseTemplates(absSrvRootDir) + + if err != nil { + return nil, err + } + app := &App{ EventClients: make(map[string][]*websocket.Conn), Broadcast: make(chan models.EventPlaces, 1), @@ -31,7 +48,9 @@ func NewApp(slog *simplelog.Log, fbConfigOauth *oauth2.Config, FBState string, S Slog: slog, SrvRootDir: SrvRootDir, Manager: mk_session.NewManager("sessionID", 60), + AbsSrvRootDir: absSrvRootDir, + Tmpl: tmpl, } - return app + return app, err } diff --git a/app/helpers.go b/app/helpers.go index 86fe75c..d282a6a 100644 --- a/app/helpers.go +++ b/app/helpers.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "github.com/Diminho/MK_practice/models" @@ -14,17 +15,8 @@ import ( "github.com/gorilla/websocket" ) -func PopulateTemplate(data interface{}, w http.ResponseWriter, tmplFile string) { - tmpl, err := template.ParseFiles(tmplFile) - - if err != nil { - fmt.Errorf("error %v", err) - } - - if tmpl != nil { - tmpl.Execute(w, data) - } - +func PopulateTemplate(tmpl *template.Template, data interface{}, w http.ResponseWriter, tmplName string) { + tmpl.ExecuteTemplate(w, tmplName, data) } func InStringSlice(input []string, needle string) bool { @@ -106,3 +98,21 @@ func RemoveContents(dir string) error { } return nil } + +func ParseTemplates(dir string) (*template.Template, error) { + var err error + tmpl := template.New("") + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if strings.Contains(path, ".html") { + _, err = tmpl.ParseFiles(path) + + if err != nil { + return err + } + } + + return err + }) + + return tmpl, err +} diff --git a/main.go b/main.go index 1fff784..7b33ae9 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "log" "os" @@ -15,7 +16,8 @@ import ( ) func main() { - // os.RemoveAll("/tmp/") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { log.Fatal("Failed to open log file: ", err) @@ -30,21 +32,27 @@ func main() { slog := simplelog.NewLog(file) db, err := models.Connect(config.Driver, fmt.Sprintf("%s:@%s/%s", config.User, config.Host, config.DbName)) + + if err != nil { + slog.Fatal(err) + } + + app, err := app.NewApp( + slog, + &oauth2.Config{ + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + RedirectURL: config.RedirectURL, + Scopes: config.Scopes, + Endpoint: config.Endpoint, + }, + config.FBState, + config.ServerRootDir, + db.Instance()) + if err != nil { slog.Fatal(err) } - mk_server.NewServer( - app.NewApp( - slog, - &oauth2.Config{ - ClientID: config.ClientID, - ClientSecret: config.ClientSecret, - RedirectURL: config.RedirectURL, - Scopes: config.Scopes, - Endpoint: config.Endpoint, - }, - config.FBState, - config.ServerRootDir, - db.Instance())) + mk_server.NewServer(ctx, app) } diff --git a/mk_server/mk_server.go b/mk_server/mk_server.go index b60c4b2..247d091 100644 --- a/mk_server/mk_server.go +++ b/mk_server/mk_server.go @@ -10,10 +10,10 @@ import ( "net/url" "os" "os/signal" - "path/filepath" "strings" "sync" "syscall" + "time" "github.com/Diminho/MK_practice/app" "github.com/Diminho/MK_practice/models" @@ -33,24 +33,21 @@ var upgrader = websocket.Upgrader{ }, } -func NewServer(app *app.App) { - +func NewServer(ctx context.Context, app *app.App) { + ctx, cancel := context.WithCancel(ctx) wApp := &WraperApp{app} s := &http.Server{Addr: ":8000", Handler: LoadRoutes(wApp)} go wApp.handlePlaceBookings() - ctx, cancel := context.WithCancel(context.Background()) - go func() { wApp.Slog.Info("Server running on port :8000") if err := s.ListenAndServe(); err != http.ErrServerClosed { wApp.Slog.Error(err) - cancel() - } + cancel() }() var gracefulShut = make(chan os.Signal, 1) signal.Notify(gracefulShut, os.Interrupt, syscall.SIGTERM) @@ -65,30 +62,29 @@ func NewServer(app *app.App) { } func (wApp *WraperApp) graceful(ctx context.Context, hs *http.Server, slog *simplelog.Log) { - for event := range wApp.EventClients { - for _, client := range wApp.EventClients[event] { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + for _, events := range wApp.EventClients { + for _, client := range events { if err := client.Close(); err != nil { slog.Error(err) } } - delete(wApp.EventClients, event) } if err := hs.Shutdown(ctx); err != nil { slog.Error(err) } - app.RemoveContents("./tmp/") + + close(wApp.Broadcast) + app.RemoveContents(os.TempDir()) slog.Info("Server stopped") } func LoadRoutes(wApp *WraperApp) http.Handler { - absSrvRootDir, err := filepath.Abs(wApp.SrvRootDir) - if err != nil { - wApp.Slog.Error(err) - } - //need this assignemt since wApp.AbsSrvRootDir is used in application - wApp.AbsSrvRootDir = absSrvRootDir + fs := http.FileServer(http.Dir(wApp.AbsSrvRootDir)) mux := http.NewServeMux() mux.Handle("/", fs) @@ -213,7 +209,7 @@ func (wApp *WraperApp) handleEvent(w http.ResponseWriter, r *http.Request) { tmplData.UserInfo["id"] = user.ID } - app.PopulateTemplate(tmplData, w, fmt.Sprintf("%s/event.html", wApp.AbsSrvRootDir)) + app.PopulateTemplate(wApp.Tmpl, tmplData, w, "event") } func (wApp *WraperApp) handleConnections(w http.ResponseWriter, r *http.Request) { diff --git a/mk_session/file_provider.go b/mk_session/file_provider.go index 23c9623..72fbb3a 100644 --- a/mk_session/file_provider.go +++ b/mk_session/file_provider.go @@ -2,9 +2,10 @@ package mk_session import ( "encoding/gob" - "log" "os" "time" + + "github.com/pkg/errors" ) type FileProvider struct { @@ -13,32 +14,28 @@ type FileProvider struct { expirationTime time.Time } -func (fp *FileProvider) Init() error { +func (fp *FileProvider) Init() (err error) { file, err := os.OpenFile(fp.filename, os.O_CREATE, 0666) if err != nil { - log.Println("Failed to open log file: ", err) + return } + defer func() { - err := file.Close() - if err != nil { - log.Println(err) - } + err = file.Close() }() - return err + + return } -func (fp *FileProvider) Save(key string, value interface{}) error { +func (fp *FileProvider) Save(key string, value interface{}) (err error) { file, err := os.OpenFile(fp.filename, os.O_RDWR|os.O_APPEND, 0666) if err != nil { - log.Println("Failed to open log file: ", err) + return errors.Wrap(err, "cannot create session file") } defer func() { - err := file.Close() - if err != nil { - log.Println(err) - } + err = file.Close() }() data := make(map[string]interface{}) @@ -51,53 +48,46 @@ func (fp *FileProvider) Save(key string, value interface{}) error { data[key] = value encoder := gob.NewEncoder(file) - if err := encoder.Encode(data); err != nil { - log.Println(err) - } + err = encoder.Encode(data) + return err } -func (fp *FileProvider) Read(key string) (interface{}, error) { - var err error +func (fp *FileProvider) Read(key string) (value interface{}, err error) { file, err := os.OpenFile(fp.filename, os.O_RDONLY, 0666) if err != nil { - log.Println("Failed to open log file: ", err) + return value, errors.Wrap(err, "cannot open session file") } defer func() { err = file.Close() - if err != nil { - log.Println(err) - } }() data := make(map[string]interface{}) decoder := gob.NewDecoder(file) err = decoder.Decode(&data) + if err != nil { - log.Println(err) + return } - return data[key], err + value = data[key] + return value, err } -func (fp *FileProvider) Delete(key string) error { - var err error +func (fp *FileProvider) Delete(key string) (err error) { file, err := os.OpenFile(fp.filename, os.O_RDWR, 0666) if err != nil { - log.Println("Failed to open log file: ", err) + return errors.Wrap(err, "cannot open session file") } defer func() { - err := file.Close() - if err != nil { - log.Println(err) - } + err = file.Close() }() data := make(map[string]interface{}) @@ -109,16 +99,16 @@ func (fp *FileProvider) Delete(key string) error { delete(data, key) encoder := gob.NewEncoder(file) - if err := encoder.Encode(data); err != nil { - log.Println(err) - } + err = encoder.Encode(data) return err } -func (fp *FileProvider) EraseByExpiration() { - err := os.Remove(fp.filename) - if err != nil { - log.Println("Failed to remove file: ", err) - } +func (fp *FileProvider) EraseByExpiration() (err error) { + err = os.Remove(fp.filename) + return +} + +func generateSessFilename(sessID string) string { + return os.TempDir() + "/sess_" + sessID } diff --git a/mk_session/mk_session.go b/mk_session/mk_session.go index e9062fe..11e0da5 100644 --- a/mk_session/mk_session.go +++ b/mk_session/mk_session.go @@ -4,9 +4,10 @@ import ( "crypto/rand" "encoding/base64" "io" - "log" "net/http" "time" + + "github.com/pkg/errors" ) type Session interface { @@ -25,7 +26,7 @@ type Provider interface { Save(string, interface{}) error Read(string) (interface{}, error) Delete(key string) error - EraseByExpiration() + EraseByExpiration() error Init() error } @@ -40,27 +41,28 @@ func NewManager(cookieName string, maxLifeTime int) *Manager { func (mng *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (ins *Instance, err error) { ins = &Instance{} - cookie, errNoCookie := r.Cookie(mng.cookieName) - if errNoCookie == nil { - ins.sessID = cookie.Value - ins.SetProvider(&FileProvider{filename: "../tmp/sess_" + ins.sessID}) + cookie, err := r.Cookie(mng.cookieName) + if err != nil && err != http.ErrNoCookie { + return nil, errors.Wrap(err, "cannot get cookie") + } + + if err == http.ErrNoCookie { + ins.sessID = NewSessionID() + cookie := http.Cookie{Name: mng.cookieName, Value: ins.sessID, Expires: (time.Now().Add(time.Duration(mng.maxLifeTime) * time.Second))} + http.SetCookie(w, &cookie) + ins.SetProvider(&FileProvider{filename: generateSessFilename(ins.sessID)}) err = ins.provider.Init() if err != nil { return nil, err } - } else { - ins.sessID = NewSessionID() - cookie := http.Cookie{Name: mng.cookieName, Value: ins.sessID, Expires: (time.Now().Add(time.Duration(mng.maxLifeTime) * time.Second))} - http.SetCookie(w, &cookie) - ins.SetProvider(&FileProvider{filename: "../tmp/sess_" + ins.sessID}) - //delete session when time expires - go func() { - time.AfterFunc(time.Duration(mng.maxLifeTime)*time.Second, func() { ins.provider.EraseByExpiration() }) - }() - + _ = time.AfterFunc(time.Duration(mng.maxLifeTime)*time.Second, func() { _ = ins.provider.EraseByExpiration() }) + return } + ins.sessID = cookie.Value + ins.SetProvider(&FileProvider{filename: generateSessFilename(ins.sessID)}) + return } @@ -69,32 +71,17 @@ func (ins *Instance) SetProvider(p Provider) { } func (ins *Instance) Set(key string, value interface{}) error { - err := ins.provider.Save(key, value) - - if err != nil { - log.Println(err) - } - - return err + return errors.Wrap(ins.provider.Save(key, value), "cannot save value") } func (ins *Instance) Delete(key string) error { - err := ins.provider.Delete(key) - if err != nil { - log.Println(err) - } - - return err + return errors.Wrap(ins.provider.Delete(key), "cannot delete value") } func (ins *Instance) Get(key string) (value interface{}, err error) { value, err = ins.provider.Read(key) - - if err != nil { - log.Println(err) - } - + err = errors.Wrap(err, "cannot read value") return } diff --git a/models/db.go b/models/db.go index 5eef1bd..f6ad30e 100644 --- a/models/db.go +++ b/models/db.go @@ -21,6 +21,7 @@ type DB struct { *sql.DB driver string dsn string + Error error } func Connect(driver, dataSourceName string) (*DB, error) { @@ -41,7 +42,7 @@ func (db *DB) Instance() func() Database { var connected bool connected, err := db.isAlive() if err != nil { - panic(err) + db.Error = err } retriesLeft := retriesNumber @@ -49,21 +50,21 @@ func (db *DB) Instance() func() Database { for connected != true { // reconnect if we lost connection err := db.Close() if err != nil { - panic(err) + db.Error = err } time.Sleep(3 * time.Second) db, err = Connect(db.driver, db.dsn) if err != nil { - panic(err) + db.Error = err } connected, err = db.isAlive() if err != nil { - panic(err) + db.Error = err } if retriesLeft == 0 { err := db.Close() if err != nil { - panic(err) + db.Error = err } break } diff --git a/models/event.go b/models/event.go index 747cc0c..94e5a53 100644 --- a/models/event.go +++ b/models/event.go @@ -2,7 +2,6 @@ package models import ( "fmt" - "log" "strings" "sync" ) @@ -43,6 +42,9 @@ type EventSystemMessage struct { } func (db *DB) OccupiedPlacesInEvent() (eventRow []EventPlacesRow, err error) { + if db.Error != nil { + return nil, db.Error + } sqlStatement := "select placeIdentity, isBooked, isBought, userId, eventId from event_places where isBooked = 1 OR isBought = 1" event, err := queryEvent(sqlStatement, db) eventRow = event.EventPlacesRows @@ -50,6 +52,9 @@ func (db *DB) OccupiedPlacesInEvent() (eventRow []EventPlacesRow, err error) { } func (db *DB) AllPlacesInEvent() (event EventPlacesTemplate, err error) { + if db.Error != nil { + return event, db.Error + } sqlStatement := "select placeIdentity, isBooked, isBought, userId, eventId from event_places" event, err = queryEvent(sqlStatement, db) event.UserInfo = make(map[string]string) @@ -80,7 +85,6 @@ func queryEvent(sqlStatement string, db *DB) (EventPlacesTemplate, error) { err = rows.Err() if err != nil { - log.Fatal(err) return templateRows, err } @@ -89,7 +93,9 @@ func queryEvent(sqlStatement string, db *DB) (EventPlacesTemplate, error) { func (db *DB) ProcessPlace(places *EventPlaces, user string) (StatusCode, error) { var sqlStatement string - + if db.Error != nil { + return 1, db.Error + } switch places.Action { case "buy": sqlStatement = "UPDATE event_places set isBought = 1, isBooked = 0 WHERE placeIdentity = ?" diff --git a/models/user.go b/models/user.go index 2439fc2..097e54f 100644 --- a/models/user.go +++ b/models/user.go @@ -2,7 +2,6 @@ package models import ( "database/sql" - "log" ) type User struct { @@ -15,38 +14,40 @@ type User struct { func (db *DB) UserExists(email string) (bool, error) { var id int isExists := true + if db.Error != nil { + return false, db.Error + } err := db.QueryRow("SELECT id FROM users WHERE email = ?", email).Scan(&id) if err != nil { if err == sql.ErrNoRows { isExists = false - } else { - log.Println(err) } } + return isExists, err } func (db *DB) FindUserByEmail(email string) (User, error) { var user User var err error - err = db.QueryRow("SELECT name, email, id FROM users WHERE email = ?", email).Scan(&user.Name, &user.Email, &user.ID) - if err != nil { - if err != sql.ErrNoRows { - log.Println(err) - } + if db.Error != nil { + return user, db.Error } + err = db.QueryRow("SELECT name, email, id FROM users WHERE email = ?", email).Scan(&user.Name, &user.Email, &user.ID) return user, err } func (db *DB) AddNewUser(user *User) error { var err error + if db.Error != nil { + return db.Error + } stmt, err := db.Prepare("INSERT INTO users(name, email, facebookId) VALUES(?, ?, ?)") + if err != nil { - log.Println(err) + return err } _, err = stmt.Exec(user.Name, user.Email, user.FacebookID) - if err != nil { - log.Println(err) - } + return err } diff --git a/public/event.html b/public/event.html index bd939ea..e95a32f 100644 --- a/public/event.html +++ b/public/event.html @@ -1,5 +1,6 @@ +{{define "event"}}
@@ -85,4 +86,5 @@