Files
drive/apps/backend/internal/drexa/server.go

139 lines
4.1 KiB
Go

package drexa
import (
"context"
"fmt"
"strings"
"github.com/get-drexa/drexa/internal/account"
"github.com/get-drexa/drexa/internal/auth"
"github.com/get-drexa/drexa/internal/blob"
"github.com/get-drexa/drexa/internal/catalog"
"github.com/get-drexa/drexa/internal/database"
"github.com/get-drexa/drexa/internal/httperr"
"github.com/get-drexa/drexa/internal/upload"
"github.com/get-drexa/drexa/internal/user"
"github.com/get-drexa/drexa/internal/virtualfs"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/uptrace/bun"
"github.com/uptrace/bun/extra/bundebug"
)
type Server struct {
config Config
app *fiber.App
db *bun.DB
blobStore blob.Store
vfs *virtualfs.VirtualFS
userService *user.Service
authService *auth.Service
accountService *account.Service
uploadService *upload.Service
authMiddleware fiber.Handler
}
func NewServer(c Config) (*Server, error) {
app := fiber.New(fiber.Config{
ErrorHandler: httperr.ErrorHandler,
StreamRequestBody: true,
// Trust proxy headers (X-Forwarded-Proto, X-Forwarded-For) for proper
// protocol detection behind reverse proxies, tunnels (ngrok, cloudflare), etc.
EnableTrustedProxyCheck: true,
TrustedProxies: []string{"127.0.0.1", "::1"},
ProxyHeader: fiber.HeaderXForwardedFor,
})
app.Use(logger.New())
// Configure CORS middleware
corsConfig := cors.Config{
AllowOrigins: "",
AllowCredentials: c.CORS.AllowCredentials,
}
if len(c.CORS.AllowOrigins) > 0 {
corsConfig.AllowOrigins = strings.Join(c.CORS.AllowOrigins, ",")
}
app.Use(cors.New(corsConfig))
db := database.NewFromPostgres(c.Database.PostgresURL)
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
// Initialize blob store based on config
var blobStore blob.Store
switch c.Storage.Backend {
case StorageBackendFS:
blobStore = blob.NewFSStore(blob.FSStoreConfig{
Root: c.Storage.RootPath,
})
case StorageBackendS3:
return nil, fmt.Errorf("s3 storage backend not yet implemented")
default:
return nil, fmt.Errorf("unknown storage backend: %s", c.Storage.Backend)
}
err := blobStore.Initialize(context.TODO())
if err != nil {
return nil, fmt.Errorf("failed to initialize blob store: %w", err)
}
// Initialize key resolver based on config
var keyResolver virtualfs.BlobKeyResolver
switch c.Storage.Mode {
case StorageModeFlat:
keyResolver = virtualfs.NewFlatKeyResolver()
case StorageModeHierarchical:
keyResolver = virtualfs.NewHierarchicalKeyResolver(db)
default:
return nil, fmt.Errorf("unknown storage mode: %s", c.Storage.Mode)
}
vfs, err := virtualfs.New(blobStore, keyResolver)
if err != nil {
return nil, fmt.Errorf("failed to create virtual file system: %w", err)
}
userService := user.NewService()
authService := auth.NewService(userService, auth.TokenConfig{
Issuer: c.JWT.Issuer,
Audience: c.JWT.Audience,
SecretKey: c.JWT.SecretKey,
})
uploadService := upload.NewService(vfs, blobStore)
accountService := account.NewService(userService, vfs)
cookieConfig := auth.CookieConfig{
Domain: c.Cookie.Domain,
Secure: c.Cookie.Secure,
}
authMiddleware := auth.NewAuthMiddleware(authService, db, cookieConfig)
api := app.Group("/api")
auth.NewHTTPHandler(authService, db, cookieConfig).RegisterRoutes(api)
user.NewHTTPHandler(userService, db, authMiddleware).RegisterRoutes(api)
accountRouter := account.NewHTTPHandler(accountService, authService, db, authMiddleware, cookieConfig).RegisterRoutes(api)
upload.NewHTTPHandler(uploadService, db).RegisterRoutes(accountRouter)
catalog.NewHTTPHandler(vfs, db).RegisterRoutes(accountRouter)
s := &Server{
config: c,
app: app,
db: db,
blobStore: blobStore,
vfs: vfs,
userService: userService,
authService: authService,
accountService: accountService,
uploadService: uploadService,
authMiddleware: authMiddleware,
}
return s, nil
}
func (s *Server) Start() error {
return s.app.Listen(fmt.Sprintf(":%d", s.config.Server.Port))
}