2022-05-01 15:50:59 +02:00

130 lines
2.6 KiB
Go

package api
import (
"context"
"math/rand"
"net/http"
"os"
"os/signal"
"time"
"github.com/gin-gonic/gin"
photoserrors "gitlab.celogeek.com/photos/api/internal/photos/errors"
"gitlab.celogeek.com/photos/api/internal/photos/store"
"gorm.io/gorm"
)
type Service struct {
Gin *gin.Engine
DB *gorm.DB
Config *ServiceConfig
Store *store.Store
LogOk *Logger
LogErr *Logger
}
type ServiceConfig struct {
Listen string
DB *DBConfig
StorePath string
}
func New(config *ServiceConfig) *Service {
return &Service{
Gin: gin.New(),
Config: config,
Store: &store.Store{Path: config.StorePath},
LogOk: &Logger{os.Stdout, "Photos"},
LogErr: &Logger{os.Stderr, "Photos"},
}
}
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,
})
})
file := s.Gin.Group("/file")
file.Use(s.RequireSession)
file.POST("", s.FileCreate)
file.HEAD("/:checksum", s.FileExists)
file.GET("/:checksum", s.FileGet)
file.POST("/chunk", s.FileCreateChunk)
file.HEAD("/chunk/:checksum", s.FileChunkExists)
file.GET("/analyze/:checksum", s.FileAnalyze)
s.Gin.NoRoute(func(c *gin.Context) {
s.Error(c, http.StatusNotFound, photoserrors.ErrReqNotFound)
})
}
func (s *Service) PrepareStore() {
d, err := os.Stat(s.Store.Path)
if err != nil {
s.LogErr.Fatal("Store", err)
}
if !d.IsDir() {
s.LogErr.Fatal("Store", photoserrors.ErrStorePathNotADirectory)
}
}
func (s *Service) Run() error {
rand.Seed(time.Now().UnixNano())
s.PrepareStore()
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
}