- go.mod module path (and every internal/vibetree import) inventree -> vibetree - cmd/inventree/ -> cmd/vibetree/, deploy/inventree.service -> vibetree.service - Default site name (seed data + the siteName template func's fallback) is now "vibetree", shown in the navbar, <title>, and login page - Session cookie renamed inventree_session -> vibetree_session - Default DB path data/inventree.db -> data/vibetree.db, CLI/log text, Dockerfile, docker-compose.yml, README, .gitignore/.dockerignore all updated to match Existing installs: this changes the session cookie name (users will be signed out once) and the default DB filename (point -db at your existing file, or rename it, to keep your data).
92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
// Package web implements the HTTP server: routing, session middleware,
|
|
// template rendering, and all page/API handlers for vibetree.
|
|
package web
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"vibetree/internal/assets"
|
|
"vibetree/internal/auth"
|
|
"vibetree/internal/folders"
|
|
"vibetree/internal/itemtypes"
|
|
"vibetree/internal/settings"
|
|
"vibetree/internal/statuses"
|
|
"vibetree/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(app)
|
|
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)
|
|
}
|