Add file upload portal with per-customer links and WebDAV admin access

- Customer upload via token link (no login), optional password + expiry,
  drag & drop for files and folders with preserved structure
- Admin portal with setup wizard, role-based users (admin/staff),
  per-customer WebDAV access rules (read/write), session auth
- WebDAV container (Debian apache2) with htpasswd + access.conf
  auto-generated from the SQLite DB and reloaded via inotifywait
- Configurable public base URL and janitor cron interval in admin UI;
  janitor reconciles the uploads table with the filesystem

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker
2026-04-16 11:00:51 +02:00
parent 16795137d5
commit 0770259d3d
16 changed files with 1733 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
const DB_PATH = process.env.DB_PATH || '/data/db/app.db';
fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
db.exec(`
CREATE TABLE IF NOT EXISTS customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
token TEXT NOT NULL UNIQUE,
password_hash TEXT,
expires_at INTEGER,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS uploads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER NOT NULL,
filename TEXT NOT NULL,
relative_path TEXT NOT NULL,
size INTEGER NOT NULL,
uploaded_at INTEGER NOT NULL,
FOREIGN KEY(customer_id) REFERENCES customers(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'staff',
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS customer_access (
customer_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
access TEXT NOT NULL DEFAULT 'read',
PRIMARY KEY(customer_id, user_id),
FOREIGN KEY(customer_id) REFERENCES customers(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
);
`);
module.exports = db;