// Package web implements the HTTP server: routing, session middleware, // template rendering, and all page/API handlers for inventree. package web import ( "context" "database/sql" "log/slog" "net/http" "time" "inventree/internal/assets" "inventree/internal/auth" "inventree/internal/folders" "inventree/internal/itemtypes" "inventree/internal/settings" "inventree/internal/statuses" "inventree/internal/users" ) // App holds every dependency HTTP handlers need: data stores, templates, and // server-wide configuration. type App struct { DB *sql.DB Auth *auth.Store Users *users.Store Folders *folders.Store Assets *assets.Store ItemTypes *itemtypes.Store Statuses *statuses.Store Settings *settings.Store templates *templateSet Log *slog.Logger // SecureCookies controls the Secure flag on the session cookie; set // true when serving over HTTPS. SecureCookies bool } // NewApp wires all stores together from a shared database handle. func NewApp(db *sql.DB, log *slog.Logger, secureCookies bool) (*App, error) { userStore := users.NewStore(db) folderStore := folders.NewStore(db) settingsStore := settings.NewStore(db) app := &App{ DB: db, Users: userStore, Folders: folderStore, ItemTypes: itemtypes.NewStore(db), Statuses: statuses.NewStore(db), Settings: settingsStore, Assets: assets.NewStore(db, settingsStore, folderStore), Auth: auth.NewStore(db, userStore), Log: log, SecureCookies: secureCookies, } tmpl, err := loadTemplates() if err != nil { return nil, err } app.templates = tmpl return app, nil } // StartSessionJanitor periodically removes expired sessions until ctx (via // the returned stop channel) signals shutdown. func (a *App) StartSessionJanitor(stop <-chan struct{}) { ticker := time.NewTicker(1 * time.Hour) go func() { defer ticker.Stop() for { select { case <-ticker.C: if err := a.Auth.CleanExpired(context.Background()); err != nil { a.Log.Warn("session cleanup failed", "error", err) } case <-stop: return } } }() } func writeErr(w http.ResponseWriter, status int, msg string) { http.Error(w, msg, status) }