155 lines
4.4 KiB
Go
155 lines
4.4 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"allaboutapps.dev/aw/go-starter/internal/config"
|
|
"allaboutapps.dev/aw/go-starter/internal/data/dto"
|
|
"allaboutapps.dev/aw/go-starter/internal/data/local"
|
|
"allaboutapps.dev/aw/go-starter/internal/i18n"
|
|
"allaboutapps.dev/aw/go-starter/internal/mailer"
|
|
"allaboutapps.dev/aw/go-starter/internal/metrics"
|
|
"allaboutapps.dev/aw/go-starter/internal/push"
|
|
"allaboutapps.dev/aw/go-starter/internal/util"
|
|
"github.com/dropbox/godropbox/time2"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
// Import postgres driver for database/sql package
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
type Router struct {
|
|
Routes []*echo.Route
|
|
Root *echo.Group
|
|
Management *echo.Group
|
|
APIV1Auth *echo.Group
|
|
APIV1Push *echo.Group
|
|
WellKnown *echo.Group
|
|
}
|
|
|
|
// Server is a central struct keeping all the dependencies.
|
|
// It is initialized with wire, which handles making the new instances of the components
|
|
// in the right order. To add a new component, 3 steps are required:
|
|
// - declaring it in this struct
|
|
// - adding a provider function in providers.go
|
|
// - adding the provider's function name to the arguments of wire.Build() in wire.go
|
|
//
|
|
// Components labeled as `wire:"-"` will be skipped and have to be initialized after the InitNewServer* call.
|
|
// For more information about wire refer to https://pkg.go.dev/github.com/google/wire
|
|
type Server struct {
|
|
// skip wire:
|
|
// -> initialized with router.Init(s) function
|
|
Echo *echo.Echo `wire:"-"`
|
|
Router *Router `wire:"-"`
|
|
|
|
Config config.Server
|
|
DB *sql.DB
|
|
Mailer *mailer.Mailer
|
|
Push *push.Service
|
|
I18n *i18n.Service
|
|
Clock time2.Clock
|
|
Auth AuthService
|
|
Local *local.Service
|
|
Metrics *metrics.Service
|
|
}
|
|
|
|
// newServerWithComponents is used by wire to initialize the server components.
|
|
// Components not listed here won't be handled by wire and should be initialized separately.
|
|
// Components which shouldn't be handled must be labeled `wire:"-"` in Server struct.
|
|
func newServerWithComponents(
|
|
cfg config.Server,
|
|
db *sql.DB,
|
|
mail *mailer.Mailer,
|
|
pusher *push.Service,
|
|
i18n *i18n.Service,
|
|
clock time2.Clock,
|
|
auth AuthService,
|
|
local *local.Service,
|
|
metrics *metrics.Service,
|
|
) *Server {
|
|
return &Server{
|
|
Config: cfg,
|
|
DB: db,
|
|
Mailer: mail,
|
|
Push: pusher,
|
|
I18n: i18n,
|
|
Clock: clock,
|
|
Auth: auth,
|
|
Local: local,
|
|
Metrics: metrics,
|
|
}
|
|
}
|
|
|
|
type AuthService interface {
|
|
GetAppUserProfile(ctx context.Context, id string) (*dto.AppUserProfile, error)
|
|
InitPasswordReset(ctx context.Context, request dto.InitPasswordResetRequest) (dto.InitPasswordResetResult, error)
|
|
Login(ctx context.Context, request dto.LoginRequest) (dto.LoginResult, error)
|
|
Logout(ctx context.Context, request dto.LogoutRequest) error
|
|
Refresh(ctx context.Context, request dto.RefreshRequest) (dto.LoginResult, error)
|
|
Register(ctx context.Context, request dto.RegisterRequest) (dto.RegisterResult, error)
|
|
CompleteRegister(ctx context.Context, request dto.CompleteRegisterRequest) (dto.LoginResult, error)
|
|
DeleteUserAccount(ctx context.Context, request dto.DeleteUserAccountRequest) error
|
|
ResetPassword(ctx context.Context, request dto.ResetPasswordRequest) (dto.LoginResult, error)
|
|
UpdatePassword(ctx context.Context, request dto.UpdatePasswordRequest) (dto.LoginResult, error)
|
|
}
|
|
|
|
func NewServer(config config.Server) *Server {
|
|
s := &Server{
|
|
Config: config,
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *Server) Ready() bool {
|
|
if err := util.IsStructInitialized(s); err != nil {
|
|
log.Debug().Err(err).Msg("Server is not fully initialized")
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (s *Server) Start() error {
|
|
if !s.Ready() {
|
|
return errors.New("server is not ready")
|
|
}
|
|
|
|
if err := s.Echo.Start(s.Config.Echo.ListenAddress); err != nil {
|
|
return fmt.Errorf("failed to start echo server: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) Shutdown(ctx context.Context) []error {
|
|
log.Warn().Msg("Shutting down server")
|
|
|
|
var errs []error
|
|
|
|
if s.DB != nil {
|
|
log.Debug().Msg("Closing database connection")
|
|
|
|
if err := s.DB.Close(); err != nil && !errors.Is(err, sql.ErrConnDone) {
|
|
log.Error().Err(err).Msg("Failed to close database connection")
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
|
|
if s.Echo != nil {
|
|
log.Debug().Msg("Shutting down echo server")
|
|
|
|
if err := s.Echo.Shutdown(ctx); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Error().Err(err).Msg("Failed to shutdown echo server")
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
|
|
return errs
|
|
}
|