221 lines
10 KiB
JavaScript
221 lines
10 KiB
JavaScript
const Database = require('better-sqlite3');
|
|
const bcrypt = require('bcryptjs');
|
|
const path = require('path');
|
|
|
|
const db = new Database(process.env.DB_PATH || '/data/dickendock.db');
|
|
db.pragma('journal_mode = WAL');
|
|
db.pragma('foreign_keys = ON');
|
|
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL DEFAULT 'user',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Dashboard-Widgets
|
|
CREATE TABLE IF NOT EXISTS quick_links (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
icon TEXT NOT NULL DEFAULT '🔗',
|
|
sort_order INTEGER DEFAULT 0
|
|
);
|
|
CREATE TABLE IF NOT EXISTS todos (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
text TEXT NOT NULL,
|
|
done INTEGER NOT NULL DEFAULT 0,
|
|
sort_order INTEGER DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
|
|
content TEXT NOT NULL DEFAULT '',
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Tool: 3D-Kalkulator
|
|
CREATE TABLE IF NOT EXISTS calendar_feeds (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
color TEXT NOT NULL DEFAULT '#4ecdc4',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS folders (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
parent_id INTEGER REFERENCES folders(id) ON DELETE CASCADE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS folder_shares (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
folder_id INTEGER NOT NULL REFERENCES folders(id) ON DELETE CASCADE,
|
|
shared_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
shared_with INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(folder_id, shared_with)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS files (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
filename TEXT NOT NULL,
|
|
originalname TEXT NOT NULL,
|
|
mimetype TEXT NOT NULL DEFAULT 'application/octet-stream',
|
|
size INTEGER NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS file_shares (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
shared_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
shared_with INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(file_id, shared_with)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS admin_settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS orders (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
bemerkung TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'warteliste',
|
|
custom_price REAL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS order_items (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
order_id INTEGER NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
|
calculation_id INTEGER REFERENCES calculations(id) ON DELETE SET NULL,
|
|
calc_name TEXT NOT NULL,
|
|
preis_freundschaft REAL NOT NULL,
|
|
preis_normal REAL NOT NULL,
|
|
preis_auftrag REAL NOT NULL,
|
|
stueckzahl INTEGER NOT NULL DEFAULT 1
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS calculations (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
gramm REAL NOT NULL,
|
|
stunden REAL NOT NULL,
|
|
farben INTEGER NOT NULL DEFAULT 1,
|
|
materialpreis_pro_gramm REAL NOT NULL DEFAULT 0.01,
|
|
stromverbrauch_kw REAL NOT NULL DEFAULT 0.15,
|
|
strompreis_pro_kwh REAL NOT NULL DEFAULT 0.38,
|
|
druckerpreis REAL NOT NULL DEFAULT 550,
|
|
gesamtdruckstunden REAL NOT NULL DEFAULT 5000,
|
|
verschleiss_pro_stunde REAL NOT NULL DEFAULT 0.06,
|
|
preis_freundschaft REAL NOT NULL,
|
|
preis_normal REAL NOT NULL,
|
|
preis_auftrag REAL NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
// Standard-Admin beim ersten Start anlegen
|
|
// Default admin settings
|
|
const settingDefaults = [
|
|
['file_max_size_mb', '50'],
|
|
['file_max_count', '20'],
|
|
['file_allowed_ext', '.pdf,.jpg,.jpeg,.png,.gif,.zip,.txt,.docx,.xlsx,.mp4,.stl,.3mf'],
|
|
];
|
|
for (const [key, value] of settingDefaults) {
|
|
if (!db.prepare('SELECT key FROM admin_settings WHERE key=?').get(key))
|
|
db.prepare('INSERT INTO admin_settings (key,value) VALUES (?,?)').run(key, value);
|
|
}
|
|
|
|
if (db.prepare('SELECT COUNT(*) c FROM users').get().c === 0) {
|
|
db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)')
|
|
.run('admin', bcrypt.hashSync('admin123', 12), 'admin');
|
|
console.log('✅ Erster Start: Admin angelegt: admin / admin123 → Bitte Passwort sofort ändern!');
|
|
}
|
|
|
|
// ── Migrationen (werden bei jedem Start geprüft) ─────────────────────────────
|
|
const cols = db.prepare("PRAGMA table_info(calculations)").all().map(r => r.name);
|
|
if (!cols.includes('image')) db.exec("ALTER TABLE calculations ADD COLUMN image TEXT");
|
|
if (!cols.includes('bemerkung')) db.exec("ALTER TABLE calculations ADD COLUMN bemerkung TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Migrationen orders
|
|
const ordCols = db.prepare("PRAGMA table_info(orders)").all().map(r => r.name);
|
|
if (!ordCols.includes('bezahlt')) db.exec("ALTER TABLE orders ADD COLUMN bezahlt INTEGER NOT NULL DEFAULT 0");
|
|
if (!ordCols.includes('bezahlt_am')) db.exec("ALTER TABLE orders ADD COLUMN bezahlt_am DATETIME");
|
|
|
|
// Migrationen order_items
|
|
// files migrations
|
|
const fileCols = db.prepare("PRAGMA table_info(files)").all().map(r => r.name);
|
|
if (!fileCols.includes('folder_id')) db.exec("ALTER TABLE files ADD COLUMN folder_id INTEGER REFERENCES folders(id) ON DELETE SET NULL");
|
|
|
|
// folder_shares migrations (only if table exists)
|
|
const fsTableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='folder_shares'").get();
|
|
if (fsTableExists) {
|
|
const fsCols = db.prepare("PRAGMA table_info(folder_shares)").all().map(r => r.name);
|
|
if (!fsCols.includes('created_at')) db.exec("ALTER TABLE folder_shares ADD COLUMN created_at DATETIME DEFAULT NULL");
|
|
}
|
|
|
|
const oiCols = db.prepare("PRAGMA table_info(order_items)").all().map(r => r.name);
|
|
if (!oiCols.includes('stunden')) db.exec("ALTER TABLE order_items ADD COLUMN stunden REAL NOT NULL DEFAULT 0");
|
|
if (!oiCols.includes('custom_price')) db.exec("ALTER TABLE order_items ADD COLUMN custom_price REAL");
|
|
if (!oiCols.includes('status')) db.exec("ALTER TABLE order_items ADD COLUMN status TEXT NOT NULL DEFAULT 'warteliste'");
|
|
if (!oiCols.includes('qty_warteliste')) db.exec("ALTER TABLE order_items ADD COLUMN qty_warteliste INTEGER NOT NULL DEFAULT 0");
|
|
if (!oiCols.includes('qty_in_arbeit')) db.exec("ALTER TABLE order_items ADD COLUMN qty_in_arbeit INTEGER NOT NULL DEFAULT 0");
|
|
if (!oiCols.includes('qty_fertig')) db.exec("ALTER TABLE order_items ADD COLUMN qty_fertig INTEGER NOT NULL DEFAULT 0");
|
|
// Bestehende Einträge: alle Stücke auf Warteliste setzen falls noch keine Qtys gesetzt
|
|
db.exec("UPDATE order_items SET qty_warteliste=stueckzahl WHERE qty_warteliste=0 AND qty_in_arbeit=0 AND qty_fertig=0");
|
|
|
|
// ── Nachrichten-Migrationen ───────────────────────────────────────────────────
|
|
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages'").get()) {
|
|
db.exec(`
|
|
CREATE TABLE messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
recipient_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
encrypted_content TEXT NOT NULL,
|
|
iv TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
deleted_by_sender INTEGER NOT NULL DEFAULT 0,
|
|
deleted_by_recipient INTEGER NOT NULL DEFAULT 0,
|
|
read_by_recipient INTEGER NOT NULL DEFAULT 0
|
|
)
|
|
`);
|
|
}
|
|
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='user_keys'").get()) {
|
|
db.exec(`
|
|
CREATE TABLE user_keys (
|
|
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
public_key TEXT NOT NULL,
|
|
updated_at DATETIME DEFAULT NULL
|
|
)
|
|
`);
|
|
}
|
|
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='pushover_settings'").get()) {
|
|
db.exec(`
|
|
CREATE TABLE pushover_settings (
|
|
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
user_key TEXT NOT NULL,
|
|
app_token TEXT NOT NULL
|
|
)
|
|
`);
|
|
}
|
|
// Falls messages-Tabelle alt ist und read_by_recipient fehlt
|
|
const msgCols = db.prepare("PRAGMA table_info(messages)").all().map(r => r.name);
|
|
if (msgCols.length && !msgCols.includes('read_by_recipient'))
|
|
db.exec("ALTER TABLE messages ADD COLUMN read_by_recipient INTEGER NOT NULL DEFAULT 0");
|
|
|
|
module.exports = db;
|