first commit
This commit is contained in:
148
backend/cmd/server/main.go
Normal file
148
backend/cmd/server/main.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"filefast/backend/internal/config"
|
||||
"filefast/backend/internal/handler"
|
||||
"filefast/backend/internal/scheduler"
|
||||
"filefast/backend/internal/service"
|
||||
"filefast/backend/internal/storage"
|
||||
"filefast/backend/internal/store"
|
||||
"filefast/backend/internal/ws"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: cfg.LogLevel}))
|
||||
memStore := store.NewMemoryStore(cfg.Runtime)
|
||||
var redisBackplane *storage.RedisClient
|
||||
storageConnected := false
|
||||
redisConnected := false
|
||||
|
||||
sqliteClient, err := storage.NewSQLiteClient(cfg.SQLite)
|
||||
if err != nil {
|
||||
logger.Error("failed to initialize sqlite client", "path", cfg.SQLite.Path, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer sqliteClient.Close()
|
||||
|
||||
storagePingCtx, storagePingCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer storagePingCancel()
|
||||
if err := sqliteClient.Ping(storagePingCtx); err != nil {
|
||||
logger.Warn("sqlite connection failed", "path", cfg.SQLite.Path, "error", err)
|
||||
} else {
|
||||
storageConnected = true
|
||||
logger.Info("sqlite connected", "path", cfg.SQLite.Path)
|
||||
|
||||
bootstrapCtx, bootstrapCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer bootstrapCancel()
|
||||
|
||||
if err := sqliteClient.EnsureSchema(bootstrapCtx); err != nil {
|
||||
logger.Warn("failed to ensure sqlite schema", "error", err)
|
||||
} else {
|
||||
if err := sqliteClient.EnsureAdminUser(bootstrapCtx, cfg.Admin.Username, cfg.Admin.Password); err != nil {
|
||||
logger.Warn("failed to ensure admin user", "username", cfg.Admin.Username, "error", err)
|
||||
}
|
||||
|
||||
if err := sqliteClient.ResetOnlineDevices(bootstrapCtx); err != nil {
|
||||
logger.Warn("failed to reset device online states", "error", err)
|
||||
}
|
||||
|
||||
snapshot, err := sqliteClient.LoadSnapshot(bootstrapCtx, cfg.Runtime)
|
||||
if err != nil {
|
||||
logger.Warn("failed to restore state from sqlite", "error", err)
|
||||
} else {
|
||||
memStore.LoadSnapshot(snapshot)
|
||||
logger.Info(
|
||||
"restored state from sqlite",
|
||||
"devices", len(snapshot.Devices),
|
||||
"rooms", len(snapshot.Rooms),
|
||||
"transfers", len(snapshot.Transfers),
|
||||
"fallback_objects", len(snapshot.FallbackObjects),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
memStore.SetPersistence(sqliteClient, 5*time.Second, func(kind, id string, err error) {
|
||||
logger.Warn("failed to persist state", "kind", kind, "id", id, "error", err)
|
||||
})
|
||||
}
|
||||
|
||||
redisClient := storage.NewRedisClient(cfg.Redis)
|
||||
defer redisClient.Close()
|
||||
|
||||
redisPingCtx, redisPingCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer redisPingCancel()
|
||||
if err := redisClient.Ping(redisPingCtx); err != nil {
|
||||
logger.Warn("redis connection failed", "addr", cfg.Redis.Addr, "db", cfg.Redis.DB, "error", err)
|
||||
} else {
|
||||
redisConnected = true
|
||||
logger.Info("redis connected", "addr", cfg.Redis.Addr, "db", cfg.Redis.DB)
|
||||
redisBackplane = redisClient
|
||||
}
|
||||
|
||||
deviceService := service.NewDeviceService(memStore, redisBackplane, redisBackplane)
|
||||
roomService := service.NewRoomService(memStore, cfg.RoomTTL)
|
||||
transferService := service.NewTransferService(memStore)
|
||||
adminService := service.NewAdminService(memStore, cfg.Admin, redisBackplane, sqliteClient)
|
||||
|
||||
minioClient, err := storage.NewMinIOClient(cfg.MinIO)
|
||||
if err != nil {
|
||||
logger.Error("failed to initialize minio client", "error", err)
|
||||
}
|
||||
|
||||
hub := ws.NewHub(logger, deviceService, redisBackplane)
|
||||
go hub.Run()
|
||||
|
||||
cleanupScheduler := scheduler.NewCleanupScheduler(logger, memStore, minioClient)
|
||||
cleanupScheduler.Start()
|
||||
defer cleanupScheduler.Stop()
|
||||
|
||||
httpHandler := handler.NewHTTPHandler(handler.Dependencies{
|
||||
Config: cfg,
|
||||
Logger: logger,
|
||||
Store: memStore,
|
||||
DeviceService: deviceService,
|
||||
RoomService: roomService,
|
||||
TransferService: transferService,
|
||||
AdminService: adminService,
|
||||
MinIOClient: minioClient,
|
||||
Hub: hub,
|
||||
StorageReady: storageConnected,
|
||||
RedisReady: redisConnected,
|
||||
})
|
||||
|
||||
server := &http.Server{
|
||||
Addr: cfg.HTTPAddress,
|
||||
Handler: httpHandler.Router(),
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
go func() {
|
||||
logger.Info("server listening", "addr", cfg.HTTPAddress)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Error("server crashed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-stop
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
logger.Error("server shutdown failed", "error", err)
|
||||
}
|
||||
|
||||
logger.Info("server stopped")
|
||||
}
|
||||
Reference in New Issue
Block a user