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:
@@ -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;
|
||||
Reference in New Issue
Block a user