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:
Owen
2026-08-17 13:34:40 -05:00
commit 218687a1ee
71 changed files with 8102 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
// Package models defines the core domain types shared across inventree.
package models
import "time"
type Role string
const (
RoleAdmin Role = "admin"
RoleUser Role = "user"
)
type User struct {
ID int64
Username string
PasswordHash string
Role Role
Enabled bool
CreatedAt time.Time
UpdatedAt time.Time
}
func (u User) IsAdmin() bool { return u.Role == RoleAdmin }
type Folder struct {
ID int64
Name string
ParentID *int64
Description string
Icon string
SortOrder int
CreatedAt time.Time
UpdatedAt time.Time
// Populated by list/tree queries; not persisted directly on this table.
FolderCount int
AssetCount int
}
type Status struct {
ID int64
Name string
Color string
SortOrder int
CreatedAt time.Time
UpdatedAt time.Time
}
type FieldType string
const (
FieldTypeText FieldType = "text"
FieldTypeLongText FieldType = "long_text"
FieldTypeNumber FieldType = "number"
FieldTypeDate FieldType = "date"
FieldTypeBoolean FieldType = "boolean"
FieldTypeDropdown FieldType = "dropdown"
FieldTypeURL FieldType = "url"
FieldTypeIPAddress FieldType = "ip_address"
)
var AllFieldTypes = []FieldType{
FieldTypeText, FieldTypeLongText, FieldTypeNumber, FieldTypeDate,
FieldTypeBoolean, FieldTypeDropdown, FieldTypeURL, FieldTypeIPAddress,
}
func (t FieldType) Label() string {
switch t {
case FieldTypeText:
return "Text"
case FieldTypeLongText:
return "Long Text"
case FieldTypeNumber:
return "Number"
case FieldTypeDate:
return "Date"
case FieldTypeBoolean:
return "Boolean"
case FieldTypeDropdown:
return "Dropdown"
case FieldTypeURL:
return "URL"
case FieldTypeIPAddress:
return "IP Address"
}
return string(t)
}
type ItemType struct {
ID int64
Name string
Description string
Icon string
Enabled bool
SortOrder int
CreatedAt time.Time
UpdatedAt time.Time
Fields []CustomFieldDef
}
type CustomFieldDef struct {
ID int64
ItemTypeID int64
Name string
FieldKey string
FieldType FieldType
Required bool
DefaultValue string
Options []string
SortOrder int
CreatedAt time.Time
UpdatedAt time.Time
}
type CustomFieldValue struct {
ID int64
AssetID int64
FieldDefID int64
Value string
}
type Asset struct {
ID int64
AssetTag string
Name string
ItemTypeID int64
FolderID *int64
StatusID int64
AssignedUserID *int64
Notes string
CreatedAt time.Time
UpdatedAt time.Time
// Populated by joined list/detail queries.
ItemTypeName string
ItemTypeIcon string
StatusName string
StatusColor string
AssignedUsername string
FolderPath []BreadcrumbItem
CustomFields []AssetFieldValue
}
type AssetFieldValue struct {
Def CustomFieldDef
Value string
}
type BreadcrumbItem struct {
ID int64
Name string
}
// TreeNode is a folder-or-asset node used to render the inventory tree.
type TreeNode struct {
Type string // "folder" or "asset"
ID int64
Name string
Icon string
ParentID *int64
HasChildren bool
AssetTag string
StatusName string
StatusColor string
// Populated only when the node is pre-expanded for server-side
// recursive rendering (see web.buildTree); empty otherwise, in which
// case the client lazy-loads children via /app/tree/children.
Expanded bool
Children []TreeNode
MoreAssets int // count of additional assets beyond the tree display cap, if any
}