package api import ( "context" "log" "math/rand" "net/http" "os" "os/signal" "time" "github.com/gin-gonic/gin" gomysql "github.com/go-sql-driver/mysql" "gorm.io/gorm" gormlogger "gorm.io/gorm/logger" ) type Service struct { Gin *gin.Engine DB *gorm.DB Config *ServiceConfig LogOk *Logger LogErr *Logger GormLogger gormlogger.Interface } type ServiceConfig struct { Listen string Mysql *gomysql.Config } func New(config *ServiceConfig) *Service { return &Service{ Gin: gin.New(), Config: config, LogOk: &Logger{os.Stdout, "Photos"}, LogErr: &Logger{os.Stderr, "Photos"}, GormLogger: gormlogger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer gormlogger.Config{ SlowThreshold: time.Second, // Slow SQL threshold LogLevel: gormlogger.Error, // Log level IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger Colorful: true, // Disable color }, ), } } func (s *Service) SetupRoutes() { s.Gin.Use(gin.Logger()) s.Gin.Use(s.Recovery) s.Gin.Use(s.RequireBody) ac := s.Gin.Group("/account") ac.POST("/signup", s.Signup) ac.POST("/login", s.Login) ac.GET("/logout", s.RequireAuthToken, s.Logout) s.Gin.GET("/me", s.RequireSession, func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "success", "user": s.CurrentSession(c).Account.Login, }) }) album := s.Gin.Group("/album") album.Use(s.RequireSession) album.POST("/", s.AlbumCreate) s.Gin.NoRoute(func(c *gin.Context) { s.Error(c, http.StatusNotFound, ErrReqNotFound) }) } func (s *Service) Run() error { rand.Seed(time.Now().UnixNano()) s.SetupRoutes() s.SetupDB() go s.SessionCleaner() srv := &http.Server{ Addr: s.Config.Listen, Handler: s.Gin, } quit := make(chan os.Signal, 1) var runError error go func() { // service connections if runError = srv.ListenAndServe(); runError != nil { quit <- os.Interrupt } }() // Wait for interrupt signal to gracefully shutdown the server with // a timeout of 5 seconds. signal.Notify(quit, os.Interrupt) <-quit s.LogOk.Print("Exit", "shutdown ...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { return err } db, err := s.DB.DB() if err != nil { return err } s.LogOk.Print("Exit", "closing database ...") if err := db.Close(); err != nil { return err } s.LogOk.Print("Exit", "exiting") return runError }