Initial implementation of inventree
A small IT asset inventory system built around a nested folder tree, matching the project brief: Go + html/template + Bootstrap 5 + vanilla JS, SQLite storage (WAL, FKs, migrations), embedded templates/static assets, single compiled binary. - AJAX-driven inventory tree: lazy-loaded folder expansion with per-user persisted expand state, drag-and-drop move for folders/assets, safe delete-with-relocate-or-cascade confirmation, search with breadcrumbs. - Assets: CRUD, duplicate, move, auto-generated IT-000001-style IDs, CSV import (with optional folder auto-create) and export. - Admin-defined item types with custom fields (text/long_text/number/date/ boolean/dropdown/url/ip_address). - Configurable statuses, session-based auth (bcrypt, secure cookies), admin/user roles, user management. - CLI: serve, version, database migrate, database backup; /healthz; graceful shutdown; Dockerfile/docker-compose/systemd unit; Go test suite.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user