Scaffold Homelab Manager foundation

Monorepo (Express+TS+Drizzle/libSQL server, React+Vite+Tabler web) matching
the stack used by ScheduleTaskManager and Sloth Manager. Includes Authentik
OIDC login with local admin/operator/viewer roles (first user becomes admin),
a generalized audit log, encrypted-at-rest storage for future integration API
tokens, the DB schema for all planned modules, and the Tabler-styled app
shell/nav. Also ports the Secrets (expiry tracker) and IP Addresses (IPAM)
modules from Sloth Manager onto the new stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
bobbanandClaude Sonnet 5 committed 2026-09-14 21:57:13 +02:00
commit 6bd2ed52c1
52 files changed
+8103

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
CREATE TABLE `audit_log` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`actor_user_id` integer,
`actor_label` text,
`category` text NOT NULL,
`action` text NOT NULL,
`target_type` text,
`target_id` text,
`detail` text,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
FOREIGN KEY (`actor_user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `dns_providers` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`provider_type` text NOT NULL,
`name` text NOT NULL,
`credential_id` integer,
`config` text,
`enabled` integer DEFAULT true NOT NULL,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
FOREIGN KEY (`credential_id`) REFERENCES `integration_credentials`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `dns_records_cache` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`provider_id` integer NOT NULL,
`zone` text NOT NULL,
`record_type` text NOT NULL,
`name` text NOT NULL,
`value` text NOT NULL,
`ttl` integer,
`raw` text,
`synced_at` text DEFAULT (current_timestamp) NOT NULL,
FOREIGN KEY (`provider_id`) REFERENCES `dns_providers`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `integration_credentials` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL,
`encrypted_secret` text NOT NULL,
`created_at` text DEFAULT (current_timestamp) NOT NULL
);
--> statement-breakpoint
CREATE TABLE `integrations` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`type` text NOT NULL,
`name` text NOT NULL,
`base_url` text NOT NULL,
`credential_id` integer,
`config` text,
`enabled` integer DEFAULT true NOT NULL,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
FOREIGN KEY (`credential_id`) REFERENCES `integration_credentials`(`id`) ON UPDATE no action ON DELETE set null
);
--> statement-breakpoint
CREATE TABLE `ipam_entries` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`ip_address` text NOT NULL,
`label` text,
`vendor` text,
`location` text,
`notes` text,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
`updated_at` text DEFAULT (current_timestamp) NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `ipam_entries_ip_address_unique` ON `ipam_entries` (`ip_address`);--> statement-breakpoint
CREATE TABLE `scheduled_tasks` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`server_id` integer NOT NULL,
`schedule_type` text NOT NULL,
`origin` text DEFAULT 'agent' NOT NULL,
`name` text NOT NULL,
`command` text,
`schedule_expression` text,
`source` text,
`enabled` integer DEFAULT true NOT NULL,
`next_run_at` text,
`raw_metadata` text,
`is_stale` integer DEFAULT false NOT NULL,
`first_seen_at` text DEFAULT (current_timestamp) NOT NULL,
`last_seen_at` text DEFAULT (current_timestamp) NOT NULL,
FOREIGN KEY (`server_id`) REFERENCES `servers`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `secrets` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL,
`type` text DEFAULT 'generic' NOT NULL,
`description` text,
`expiry_date` text NOT NULL,
`warn_days` integer DEFAULT 30 NOT NULL,
`notes` text,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
`updated_at` text DEFAULT (current_timestamp) NOT NULL
);
--> statement-breakpoint
CREATE TABLE `servers` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` text NOT NULL,
`hostname` text,
`os_type` text DEFAULT 'linux' NOT NULL,
`description` text,
`api_token_hash` text NOT NULL,
`api_token_prefix` text NOT NULL,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
`last_seen_at` text
);
--> statement-breakpoint
CREATE TABLE `settings` (
`key` text PRIMARY KEY NOT NULL,
`value` text NOT NULL,
`updated_at` text DEFAULT (current_timestamp) NOT NULL
);
--> statement-breakpoint
CREATE TABLE `users` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`oidc_sub` text NOT NULL,
`email` text,
`name` text,
`role` text DEFAULT 'viewer' NOT NULL,
`created_at` text DEFAULT (current_timestamp) NOT NULL,
`last_login_at` text
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_oidc_sub_unique` ON `users` (`oidc_sub`);
+846
View File
@@ -0,0 +1,846 @@
{
"version": "6",
"dialect": "sqlite",
"id": "1480fe39-d215-4e92-aee3-4de44905a67d",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"audit_log": {
"name": "audit_log",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"actor_user_id": {
"name": "actor_user_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor_label": {
"name": "actor_label",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"category": {
"name": "category",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"action": {
"name": "action",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"target_type": {
"name": "target_type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"target_id": {
"name": "target_id",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"detail": {
"name": "detail",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {
"audit_log_actor_user_id_users_id_fk": {
"name": "audit_log_actor_user_id_users_id_fk",
"tableFrom": "audit_log",
"tableTo": "users",
"columnsFrom": [
"actor_user_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"dns_providers": {
"name": "dns_providers",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"provider_type": {
"name": "provider_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credential_id": {
"name": "credential_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"config": {
"name": "config",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {
"dns_providers_credential_id_integration_credentials_id_fk": {
"name": "dns_providers_credential_id_integration_credentials_id_fk",
"tableFrom": "dns_providers",
"tableTo": "integration_credentials",
"columnsFrom": [
"credential_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"dns_records_cache": {
"name": "dns_records_cache",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"provider_id": {
"name": "provider_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"zone": {
"name": "zone",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"record_type": {
"name": "record_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ttl": {
"name": "ttl",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"raw": {
"name": "raw",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"synced_at": {
"name": "synced_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {
"dns_records_cache_provider_id_dns_providers_id_fk": {
"name": "dns_records_cache_provider_id_dns_providers_id_fk",
"tableFrom": "dns_records_cache",
"tableTo": "dns_providers",
"columnsFrom": [
"provider_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"integration_credentials": {
"name": "integration_credentials",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"encrypted_secret": {
"name": "encrypted_secret",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"integrations": {
"name": "integrations",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"base_url": {
"name": "base_url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credential_id": {
"name": "credential_id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"config": {
"name": "config",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {
"integrations_credential_id_integration_credentials_id_fk": {
"name": "integrations_credential_id_integration_credentials_id_fk",
"tableFrom": "integrations",
"tableTo": "integration_credentials",
"columnsFrom": [
"credential_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"ipam_entries": {
"name": "ipam_entries",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"ip_address": {
"name": "ip_address",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"label": {
"name": "label",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"vendor": {
"name": "vendor",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"location": {
"name": "location",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"notes": {
"name": "notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {
"ipam_entries_ip_address_unique": {
"name": "ipam_entries_ip_address_unique",
"columns": [
"ip_address"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"scheduled_tasks": {
"name": "scheduled_tasks",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"server_id": {
"name": "server_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"schedule_type": {
"name": "schedule_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"origin": {
"name": "origin",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'agent'"
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"command": {
"name": "command",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"schedule_expression": {
"name": "schedule_expression",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"source": {
"name": "source",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"enabled": {
"name": "enabled",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"next_run_at": {
"name": "next_run_at",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"raw_metadata": {
"name": "raw_metadata",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"is_stale": {
"name": "is_stale",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"first_seen_at": {
"name": "first_seen_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
},
"last_seen_at": {
"name": "last_seen_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {
"scheduled_tasks_server_id_servers_id_fk": {
"name": "scheduled_tasks_server_id_servers_id_fk",
"tableFrom": "scheduled_tasks",
"tableTo": "servers",
"columnsFrom": [
"server_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"secrets": {
"name": "secrets",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'generic'"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"expiry_date": {
"name": "expiry_date",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"warn_days": {
"name": "warn_days",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 30
},
"notes": {
"name": "notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"servers": {
"name": "servers",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"hostname": {
"name": "hostname",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"os_type": {
"name": "os_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'linux'"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"api_token_hash": {
"name": "api_token_hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"api_token_prefix": {
"name": "api_token_prefix",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
},
"last_seen_at": {
"name": "last_seen_at",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"settings": {
"name": "settings",
"columns": {
"key": {
"name": "key",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"oidc_sub": {
"name": "oidc_sub",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'viewer'"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(current_timestamp)"
},
"last_login_at": {
"name": "last_login_at",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"users_oidc_sub_unique": {
"name": "users_oidc_sub_unique",
"columns": [
"oidc_sub"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1789415495200,
"tag": "0000_tricky_nocturne",
"breakpoints": true
}
]
}