package web import ( "net/http" content "inventree/web" ) // Router builds the complete HTTP handler for inventree: static assets, // authentication, the AJAX-driven inventory tree, and the traditional // full-page admin sections (item types, users, settings). func (a *App) Router() http.Handler { mux := http.NewServeMux() // Static assets (Bootstrap, icons, vanilla JS) and health check are // always public. mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(mustSub(content.StaticFS, "static")))) mux.HandleFunc("GET /healthz", a.handleHealthz) // Auth mux.HandleFunc("GET /login", a.handleLoginForm) mux.HandleFunc("POST /login", a.handleLoginSubmit) mux.HandleFunc("POST /logout", a.handleLogout) mux.HandleFunc("GET /{$}", a.requireLogin(a.handleRoot)) // Inventory tree (AJAX fragments) mux.HandleFunc("GET /inventory", a.requireLogin(a.handleInventoryPage)) mux.HandleFunc("GET /app/tree/children", a.requireLogin(a.handleTreeChildren)) mux.HandleFunc("POST /app/tree/collapse", a.requireLogin(a.handleTreeCollapse)) mux.HandleFunc("POST /app/tree/collapse-all", a.requireLogin(a.handleTreeCollapseAll)) mux.HandleFunc("GET /app/panel/root", a.requireLogin(a.handlePanelRoot)) mux.HandleFunc("GET /app/panel/folders/{id}", a.requireLogin(a.handlePanelFolder)) mux.HandleFunc("GET /app/panel/assets/{id}", a.requireLogin(a.handlePanelAsset)) mux.HandleFunc("GET /app/panel/assets/new", a.requireAdmin(a.handlePanelAssetNew)) mux.HandleFunc("GET /app/panel/assets/{id}/edit", a.requireAdmin(a.handlePanelAssetEdit)) mux.HandleFunc("GET /app/forms/asset-fields", a.requireLogin(a.handleAssetFieldsFragment)) mux.HandleFunc("GET /app/search", a.requireLogin(a.handleSearch)) // Folder mutations (JSON in/out) mux.HandleFunc("GET /app/folders/options", a.requireAdmin(a.handleFolderOptions)) mux.HandleFunc("GET /app/folders/{id}", a.requireAdmin(a.handleFolderGet)) mux.HandleFunc("POST /app/folders", a.requireAdmin(a.handleFolderCreate)) mux.HandleFunc("PATCH /app/folders/{id}", a.requireAdmin(a.handleFolderUpdate)) mux.HandleFunc("POST /app/folders/{id}/move", a.requireAdmin(a.handleFolderMove)) mux.HandleFunc("GET /app/folders/{id}/delete-info", a.requireAdmin(a.handleFolderDeleteInfo)) mux.HandleFunc("DELETE /app/folders/{id}", a.requireAdmin(a.handleFolderDelete)) // Asset mutations (JSON in/out) mux.HandleFunc("GET /app/assets/{id}", a.requireAdmin(a.handleAssetGet)) mux.HandleFunc("POST /app/assets", a.requireAdmin(a.handleAssetCreate)) mux.HandleFunc("PATCH /app/assets/{id}", a.requireAdmin(a.handleAssetUpdate)) mux.HandleFunc("DELETE /app/assets/{id}", a.requireAdmin(a.handleAssetDelete)) mux.HandleFunc("POST /app/assets/{id}/duplicate", a.requireAdmin(a.handleAssetDuplicate)) mux.HandleFunc("POST /app/assets/{id}/move", a.requireAdmin(a.handleAssetMove)) mux.HandleFunc("POST /app/assets/{id}/checkout", a.requireAdmin(a.handleAssetCheckout)) mux.HandleFunc("POST /app/assets/{id}/status", a.requireAdmin(a.handleAssetSetStatus)) mux.HandleFunc("POST /app/assets/{id}/quantity", a.requireAdmin(a.handleAssetQuantity)) mux.HandleFunc("GET /app/users/options", a.requireAdmin(a.handleUserOptions)) // CSV import/export mux.HandleFunc("GET /export/assets.csv", a.requireLogin(a.handleExportCSV)) mux.HandleFunc("POST /import/assets.csv", a.requireAdmin(a.handleImportCSV)) // QR asset tags: scan a printed label to jump to that asset, or print // a sheet of labels for a folder/asset. mux.HandleFunc("GET /scan/{tag}", a.requireLogin(a.handleScan)) mux.HandleFunc("GET /labels", a.requireLogin(a.handleLabelsPage)) // Settings (admin, traditional pages) - Item Types, Users, and general // settings all live under one unified /settings section with shared // sub-navigation, rather than as separate top-level nav items. mux.HandleFunc("GET /settings", a.requireAdmin(a.handleSettingsPage)) mux.HandleFunc("POST /settings/general", a.requireAdmin(a.handleSettingsGeneral)) mux.HandleFunc("GET /settings/statuses", a.requireAdmin(a.handleSettingsStatusesPage)) mux.HandleFunc("POST /settings/statuses", a.requireAdmin(a.handleStatusCreate)) mux.HandleFunc("POST /settings/statuses/{id}", a.requireAdmin(a.handleStatusUpdate)) mux.HandleFunc("POST /settings/statuses/{id}/delete", a.requireAdmin(a.handleStatusDelete)) mux.HandleFunc("POST /settings/statuses/{id}/reorder", a.requireAdmin(a.handleStatusReorder)) mux.HandleFunc("GET /settings/item-types", a.requireAdmin(a.handleItemTypesList)) mux.HandleFunc("GET /settings/item-types/new", a.requireAdmin(a.handleItemTypeNewForm)) mux.HandleFunc("POST /settings/item-types", a.requireAdmin(a.handleItemTypeCreate)) mux.HandleFunc("GET /settings/item-types/{id}/edit", a.requireAdmin(a.handleItemTypeEditForm)) mux.HandleFunc("POST /settings/item-types/{id}", a.requireAdmin(a.handleItemTypeUpdate)) mux.HandleFunc("POST /settings/item-types/{id}/delete", a.requireAdmin(a.handleItemTypeDelete)) mux.HandleFunc("POST /settings/item-types/{id}/fields", a.requireAdmin(a.handleFieldCreate)) mux.HandleFunc("POST /settings/item-types/{id}/fields/{fieldID}", a.requireAdmin(a.handleFieldUpdate)) mux.HandleFunc("POST /settings/item-types/{id}/fields/{fieldID}/delete", a.requireAdmin(a.handleFieldDelete)) mux.HandleFunc("GET /settings/import", a.requireAdmin(a.handleSettingsImportPage)) mux.HandleFunc("POST /settings/import/assets", a.requireAdmin(a.handleSettingsImportAssets)) mux.HandleFunc("POST /settings/import/statuses", a.requireAdmin(a.handleSettingsImportStatuses)) mux.HandleFunc("GET /settings/users", a.requireAdmin(a.handleUsersList)) mux.HandleFunc("GET /settings/users/new", a.requireAdmin(a.handleUserNewForm)) mux.HandleFunc("POST /settings/users", a.requireAdmin(a.handleUserCreate)) mux.HandleFunc("GET /settings/users/{id}/edit", a.requireAdmin(a.handleUserEditForm)) mux.HandleFunc("POST /settings/users/{id}", a.requireAdmin(a.handleUserUpdate)) mux.HandleFunc("POST /settings/users/{id}/password", a.requireAdmin(a.handleUserPassword)) mux.HandleFunc("POST /settings/users/{id}/delete", a.requireAdmin(a.handleUserDelete)) return a.loadSession(mux) } func (a *App) handleHealthz(w http.ResponseWriter, r *http.Request) { if err := a.DB.PingContext(r.Context()); err != nil { writeErr(w, http.StatusServiceUnavailable, "database unavailable") return } w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) } func (a *App) handleRoot(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/inventory", http.StatusSeeOther) }