110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"flag"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/auth"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/config"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/database"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/httpserver"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/logger"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/migration"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/multitable"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
//go:embed migrations/*.sql
|
|
var migrationFiles embed.FS
|
|
|
|
//go:embed web/dist/*
|
|
var frontendFiles embed.FS
|
|
|
|
//go:embed sdk/dist/*
|
|
var sdkFiles embed.FS
|
|
|
|
func main() {
|
|
environment := flag.String("env", "", "runtime environment: development, test, prod")
|
|
configDir := flag.String("config-dir", "configs", "configuration directory")
|
|
flag.Parse()
|
|
|
|
cfg, err := config.Load(config.LoadOptions{Environment: *environment, ConfigDir: *configDir})
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "load configuration: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
log, err := logger.New(cfg.App.Env, cfg.App.Name)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "create logger: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer log.Sync()
|
|
undoGlobalLogger := zap.ReplaceGlobals(log.Named("business"))
|
|
defer undoGlobalLogger()
|
|
|
|
db, err := database.Open(cfg.Database, log)
|
|
if err != nil {
|
|
log.Fatal("connect database", zap.Error(err))
|
|
}
|
|
if err := migration.Run(db, migrationFiles); err != nil {
|
|
log.Fatal("run database migrations", zap.Error(err))
|
|
}
|
|
if err := auth.Seed(db, cfg.Seed); err != nil {
|
|
log.Fatal("seed administrator", zap.Error(err))
|
|
}
|
|
workerDone := make(chan struct{})
|
|
workerCtx, stopWorker := context.WithCancel(context.Background())
|
|
if cfg.MultiTable.Enabled {
|
|
worker := multitable.NewWorker(db, cfg.MultiTable, log)
|
|
go func() {
|
|
defer close(workerDone)
|
|
log.Info("multitable synchronization started", zap.String("worker", worker.String()))
|
|
worker.Run(workerCtx)
|
|
}()
|
|
} else {
|
|
close(workerDone)
|
|
}
|
|
|
|
frontend, err := fs.Sub(frontendFiles, "web/dist")
|
|
if err != nil {
|
|
log.Fatal("load embedded frontend", zap.Error(err))
|
|
}
|
|
sdk, err := fs.Sub(sdkFiles, "sdk/dist")
|
|
if err != nil {
|
|
log.Fatal("load embedded sdk", zap.Error(err))
|
|
}
|
|
authService := auth.NewService(db, cfg.Auth)
|
|
handler := httpserver.New(db, authService, frontend, sdk, log, cfg.App.Env)
|
|
server := &http.Server{Addr: cfg.Server.Address(), Handler: handler, ReadTimeout: cfg.Server.ReadTimeout, WriteTimeout: cfg.Server.WriteTimeout}
|
|
|
|
go func() {
|
|
log.Info("server started", zap.String("address", cfg.Server.Address()))
|
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatal("server stopped unexpectedly", zap.Error(err))
|
|
}
|
|
}()
|
|
|
|
stop := make(chan os.Signal, 1)
|
|
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
|
|
<-stop
|
|
stopWorker()
|
|
ctx, cancel := context.WithTimeout(context.Background(), cfg.Server.ShutdownTimeout)
|
|
defer cancel()
|
|
if err := server.Shutdown(ctx); err != nil {
|
|
log.Error("graceful shutdown failed", zap.Error(err))
|
|
}
|
|
select {
|
|
case <-workerDone:
|
|
case <-ctx.Done():
|
|
log.Warn("multitable worker did not stop before shutdown timeout")
|
|
}
|
|
log.Info("server stopped")
|
|
}
|