Initial commit: IT Nexus Web-App
This commit is contained in:
341
backend/src/db/schema.sql
Normal file
341
backend/src/db/schema.sql
Normal file
@@ -0,0 +1,341 @@
|
||||
-- FIDO-Key Management System - Database Schema
|
||||
|
||||
-- ============================================================================
|
||||
-- ROLES TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(50) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- USERS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username VARCHAR(100) UNIQUE NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255),
|
||||
role_id INTEGER NOT NULL,
|
||||
first_name VARCHAR(100),
|
||||
last_name VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT 1,
|
||||
must_change_password BOOLEAN DEFAULT 1,
|
||||
email_notifications INTEGER NOT NULL DEFAULT 1,
|
||||
last_login DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (role_id) REFERENCES roles(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- FIDO KEYS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS fido_keys (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
serial_number VARCHAR(255) UNIQUE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL CHECK(status IN ('aktiv', 'inaktiv')),
|
||||
description TEXT,
|
||||
assigned_to_user_id INTEGER,
|
||||
created_by_user_id INTEGER NOT NULL,
|
||||
updated_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (assigned_to_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (updated_by_user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- ASSETS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS assets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL CHECK(type IN ('Notebook', 'Monitor', 'Headset', 'Other')),
|
||||
serial_number VARCHAR(255) UNIQUE NOT NULL,
|
||||
model VARCHAR(255),
|
||||
status VARCHAR(20) NOT NULL CHECK(status IN ('verfuegbar', 'zugewiesen', 'inaktiv', 'beschaedigt')),
|
||||
purchase_date DATE,
|
||||
description TEXT,
|
||||
teamviewer_id TEXT,
|
||||
assigned_to_user_id INTEGER,
|
||||
created_by_user_id INTEGER NOT NULL,
|
||||
updated_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (assigned_to_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (updated_by_user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- ASSET ASSIGNMENTS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS asset_assignments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
asset_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
assigned_by_user_id INTEGER NOT NULL,
|
||||
assigned_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
returned_at DATETIME,
|
||||
notes TEXT,
|
||||
onboarding_protocol_id INTEGER,
|
||||
offboarding_protocol_id INTEGER,
|
||||
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (assigned_by_user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- ONBOARDING PROTOCOLS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS onboarding_protocols (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
employee_user_id INTEGER NOT NULL,
|
||||
status VARCHAR(20) NOT NULL CHECK(status IN ('pending', 'in_progress', 'completed')),
|
||||
start_date DATE NOT NULL,
|
||||
completion_date DATE,
|
||||
checklist_data TEXT,
|
||||
notes TEXT,
|
||||
pdf_file_path VARCHAR(500),
|
||||
created_by_user_id INTEGER NOT NULL,
|
||||
updated_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (employee_user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (updated_by_user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- OFFBOARDING PROTOCOLS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS offboarding_protocols (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
employee_user_id INTEGER NOT NULL,
|
||||
status VARCHAR(20) NOT NULL CHECK(status IN ('pending', 'in_progress', 'completed')),
|
||||
exit_date DATE NOT NULL,
|
||||
completion_date DATE,
|
||||
checklist_data TEXT,
|
||||
notes TEXT,
|
||||
pdf_file_path VARCHAR(500),
|
||||
created_by_user_id INTEGER NOT NULL,
|
||||
updated_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (employee_user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (updated_by_user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- AUDIT LOG TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS audit_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
entity_type VARCHAR(50) NOT NULL,
|
||||
entity_id INTEGER NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
ip_address VARCHAR(45),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- INDEXES FOR PERFORMANCE
|
||||
-- ============================================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_users_role_id ON users(role_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_is_active ON users(is_active);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_fido_keys_serial ON fido_keys(serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_fido_keys_status ON fido_keys(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_fido_keys_assigned_to ON fido_keys(assigned_to_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_fido_keys_created_by ON fido_keys(created_by_user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_assets_serial ON assets(serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_assets_type ON assets(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_assets_status ON assets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_assets_assigned_to ON assets(assigned_to_user_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_assignments_asset ON asset_assignments(asset_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_assignments_user ON asset_assignments(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_asset_assignments_dates ON asset_assignments(assigned_at, returned_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_onboarding_employee ON onboarding_protocols(employee_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboarding_status ON onboarding_protocols(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_onboarding_dates ON onboarding_protocols(start_date, completion_date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_offboarding_employee ON offboarding_protocols(employee_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_offboarding_status ON offboarding_protocols(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_offboarding_dates ON offboarding_protocols(exit_date, completion_date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_log_user_id ON audit_log(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_log_entity ON audit_log(entity_type, entity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_log_created_at ON audit_log(created_at);
|
||||
|
||||
-- ============================================================================
|
||||
-- TICKETS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_number TEXT UNIQUE NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'offen'
|
||||
CHECK(status IN ('offen','in_bearbeitung','warten_auf_mitarbeiter','warten_auf_support','geschlossen')),
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'mittel'
|
||||
CHECK(priority IN ('niedrig','mittel','hoch','kritisch')),
|
||||
category VARCHAR(50) NOT NULL DEFAULT 'Allgemein'
|
||||
CHECK(category IN ('Software','Allgemein','SelectLine','Hardware')),
|
||||
source VARCHAR(10) NOT NULL DEFAULT 'web'
|
||||
CHECK(source IN ('web','email')),
|
||||
requester_name TEXT,
|
||||
requester_email TEXT,
|
||||
assigned_to_user_id INTEGER,
|
||||
created_by_user_id INTEGER,
|
||||
asset_id INTEGER,
|
||||
email_message_id TEXT,
|
||||
resolved_at DATETIME,
|
||||
closed_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (assigned_to_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- TICKET COMMENTS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS ticket_comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
user_id INTEGER,
|
||||
comment TEXT NOT NULL,
|
||||
is_internal INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- LICENSES TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS licenses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
vendor TEXT,
|
||||
license_type TEXT NOT NULL DEFAULT 'subscription'
|
||||
CHECK(license_type IN ('subscription', 'oem', 'volume', 'perpetual')),
|
||||
product_key TEXT,
|
||||
seats INTEGER,
|
||||
purchase_date DATE,
|
||||
expiry_date DATE,
|
||||
cost REAL,
|
||||
notes TEXT,
|
||||
sku_id TEXT,
|
||||
created_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_licenses_expiry ON licenses(expiry_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_licenses_type ON licenses(license_type);
|
||||
|
||||
-- ============================================================================
|
||||
-- INDEXES FOR TICKETS
|
||||
-- ============================================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_priority ON tickets(priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_created_by ON tickets(created_by_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tickets_asset ON tickets(asset_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_comments_ticket ON ticket_comments(ticket_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- MONITORING AGENTS TABLE
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS monitoring_agents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
hostname VARCHAR(255) UNIQUE NOT NULL,
|
||||
ip_address VARCHAR(50),
|
||||
mac_address VARCHAR(100),
|
||||
os_name VARCHAR(255),
|
||||
os_version VARCHAR(255),
|
||||
cpu_model VARCHAR(255),
|
||||
cpu_cores INTEGER,
|
||||
cpu_usage_percent REAL,
|
||||
ram_total_gb REAL,
|
||||
ram_used_gb REAL,
|
||||
disk_total_gb REAL,
|
||||
disk_free_gb REAL,
|
||||
last_user VARCHAR(255),
|
||||
uptime_hours REAL,
|
||||
domain VARCHAR(255),
|
||||
agent_version VARCHAR(50),
|
||||
installed_software TEXT,
|
||||
windows_updates_pending INTEGER DEFAULT 0,
|
||||
status VARCHAR(20) DEFAULT 'online',
|
||||
last_checkin DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_monitoring_agents_hostname ON monitoring_agents(hostname);
|
||||
CREATE INDEX IF NOT EXISTS idx_monitoring_agents_status ON monitoring_agents(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_monitoring_agents_last_checkin ON monitoring_agents(last_checkin);
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- NEXUS SCANNER TABLES
|
||||
-- ============================================================================
|
||||
CREATE TABLE IF NOT EXISTS scanner_sites (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
site_id VARCHAR(10) NOT NULL UNIQUE,
|
||||
last_seen DATETIME,
|
||||
scanner_version VARCHAR(50),
|
||||
host_count INTEGER DEFAULT 0,
|
||||
check_count INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scanner_assets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
site_id VARCHAR(10) NOT NULL,
|
||||
ip VARCHAR(50) NOT NULL,
|
||||
mac VARCHAR(100),
|
||||
hostname VARCHAR(255),
|
||||
vendor VARCHAR(255),
|
||||
status VARCHAR(20) DEFAULT 'online',
|
||||
first_seen DATETIME,
|
||||
last_seen DATETIME,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(site_id, ip)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS scanner_alerts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
site_id VARCHAR(10) NOT NULL,
|
||||
check_name VARCHAR(255) NOT NULL,
|
||||
check_type VARCHAR(50),
|
||||
target VARCHAR(255),
|
||||
status VARCHAR(20) NOT NULL,
|
||||
latency_ms INTEGER DEFAULT 0,
|
||||
error_msg TEXT,
|
||||
subject TEXT,
|
||||
triggered_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_scanner_assets_site ON scanner_assets(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_scanner_assets_status ON scanner_assets(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_scanner_alerts_site ON scanner_alerts(site_id, triggered_at DESC);
|
||||
726
backend/src/db/seed.js
Normal file
726
backend/src/db/seed.js
Normal file
@@ -0,0 +1,726 @@
|
||||
const Database = require('better-sqlite3');
|
||||
const bcrypt = require('bcrypt');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
require('dotenv').config();
|
||||
|
||||
const DB_PATH = process.env.DATABASE_PATH || './database.sqlite';
|
||||
const BCRYPT_ROUNDS = parseInt(process.env.BCRYPT_ROUNDS) || 12;
|
||||
|
||||
// Default Super Admin credentials
|
||||
const DEFAULT_SUPERADMIN = {
|
||||
username: 'superadmin',
|
||||
email: 'admin@fido-manager.local',
|
||||
password: 'Admin123!',
|
||||
first_name: 'Super',
|
||||
last_name: 'Administrator'
|
||||
};
|
||||
|
||||
async function initializeDatabase() {
|
||||
try {
|
||||
console.log('🔧 Initializing database...');
|
||||
|
||||
// Create database connection
|
||||
const db = new Database(DB_PATH);
|
||||
|
||||
// Read and execute schema
|
||||
const schemaPath = path.join(__dirname, 'schema.sql');
|
||||
const schema = fs.readFileSync(schemaPath, 'utf8');
|
||||
|
||||
// Execute schema (split by semicolon and execute each statement)
|
||||
const statements = schema.split(';').filter(stmt => stmt.trim());
|
||||
for (const statement of statements) {
|
||||
if (statement.trim()) {
|
||||
db.exec(statement);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Database schema created successfully');
|
||||
|
||||
// Create uploads directories
|
||||
console.log('📁 Creating uploads directories...');
|
||||
const uploadsDir = path.join(__dirname, '../../uploads');
|
||||
const onboardingDir = path.join(uploadsDir, 'onboarding');
|
||||
const offboardingDir = path.join(uploadsDir, 'offboarding');
|
||||
const currentYear = new Date().getFullYear().toString();
|
||||
|
||||
if (!fs.existsSync(uploadsDir)) {
|
||||
fs.mkdirSync(uploadsDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(onboardingDir)) {
|
||||
fs.mkdirSync(onboardingDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(offboardingDir)) {
|
||||
fs.mkdirSync(offboardingDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create year subdirectories
|
||||
const onboardingYearDir = path.join(onboardingDir, currentYear);
|
||||
const offboardingYearDir = path.join(offboardingDir, currentYear);
|
||||
|
||||
if (!fs.existsSync(onboardingYearDir)) {
|
||||
fs.mkdirSync(onboardingYearDir, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(offboardingYearDir)) {
|
||||
fs.mkdirSync(offboardingYearDir, { recursive: true });
|
||||
}
|
||||
|
||||
console.log('✅ Uploads directories created successfully');
|
||||
|
||||
// Insert roles
|
||||
console.log('📝 Inserting default roles...');
|
||||
const insertRole = db.prepare(`
|
||||
INSERT OR IGNORE INTO roles (name, description)
|
||||
VALUES (?, ?)
|
||||
`);
|
||||
|
||||
const roles = [
|
||||
['super_admin', 'Super Administrator - Full system access including user management'],
|
||||
['admin', 'Administrator - Can manage FIDO keys and view users'],
|
||||
['bearbeiter', 'Editor - Can create and edit FIDO keys'],
|
||||
['benutzer', 'User - Can only view FIDO keys'],
|
||||
['support', 'Support - Kann Tickets sehen und bearbeiten'],
|
||||
['hr_personal', 'HR / Personal - Sieht HR-Aufgaben im Onboarding'],
|
||||
['buchhaltung', 'Buchhaltung / Lohn - Sieht Buchhaltungs-Aufgaben im Onboarding'],
|
||||
['produktion', 'Produktion - Sieht und verwaltet Produktions-Assets'],
|
||||
];
|
||||
|
||||
for (const [name, description] of roles) {
|
||||
insertRole.run(name, description);
|
||||
}
|
||||
|
||||
console.log('✅ Roles created successfully');
|
||||
|
||||
// Run migrations for existing databases
|
||||
console.log('🔄 Running database migrations...');
|
||||
const migrations = [
|
||||
`ALTER TABLE assets ADD COLUMN teamviewer_id TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN azure_id TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN email_notifications INTEGER NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE assets ADD COLUMN last_maintenance_date DATE`,
|
||||
`ALTER TABLE assets ADD COLUMN next_maintenance_date DATE`,
|
||||
`ALTER TABLE assets ADD COLUMN maintenance_interval_months INTEGER`,
|
||||
`ALTER TABLE assets ADD COLUMN maintenance_notes TEXT`,
|
||||
`CREATE TABLE IF NOT EXISTS licenses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
vendor TEXT,
|
||||
license_type TEXT NOT NULL DEFAULT 'subscription',
|
||||
product_key TEXT,
|
||||
seats INTEGER,
|
||||
purchase_date DATE,
|
||||
expiry_date DATE,
|
||||
cost REAL,
|
||||
notes TEXT,
|
||||
created_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_licenses_expiry ON licenses(expiry_date)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_licenses_type ON licenses(license_type)`,
|
||||
`ALTER TABLE licenses ADD COLUMN sku_id TEXT`,
|
||||
`ALTER TABLE tickets ADD COLUMN snoozed_until DATETIME`,
|
||||
`CREATE TABLE IF NOT EXISTS ticket_links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
linked_ticket_id INTEGER NOT NULL,
|
||||
link_type TEXT NOT NULL DEFAULT 'related',
|
||||
created_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (linked_ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
UNIQUE(ticket_id, linked_ticket_id)
|
||||
)`,
|
||||
`ALTER TABLE tickets ADD COLUMN ai_suggestion TEXT`,
|
||||
`ALTER TABLE ticket_comments ADD COLUMN is_ai_comment INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE tickets ADD COLUMN ai_active INTEGER DEFAULT 1`,
|
||||
`CREATE TABLE IF NOT EXISTS ticket_assignees (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
assigned_by_user_id INTEGER,
|
||||
assigned_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(ticket_id, user_id)
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS knowledge_base (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
problem TEXT NOT NULL,
|
||||
solution TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'Allgemein',
|
||||
tags TEXT,
|
||||
created_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`,
|
||||
`ALTER TABLE tickets ADD COLUMN escalated_at DATETIME`,
|
||||
`ALTER TABLE tickets ADD COLUMN satisfaction_rating TEXT`,
|
||||
`CREATE TABLE IF NOT EXISTS ticket_routing (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category TEXT NOT NULL UNIQUE,
|
||||
assigned_to_user_id INTEGER,
|
||||
FOREIGN KEY (assigned_to_user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`,
|
||||
`ALTER TABLE onboarding_protocols ADD COLUMN dept_contacts TEXT`,
|
||||
`ALTER TABLE knowledge_base ADD COLUMN source_ticket_id INTEGER REFERENCES tickets(id) ON DELETE SET NULL`,
|
||||
`ALTER TABLE knowledge_base ADD COLUMN auto_generated INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE knowledge_base ADD COLUMN images TEXT DEFAULT '[]'`,
|
||||
`ALTER TABLE onboarding_protocols ADD COLUMN confirm_token TEXT`,
|
||||
`ALTER TABLE onboarding_protocols ADD COLUMN employee_confirmed_at DATETIME`,
|
||||
`ALTER TABLE users ADD COLUMN notif_ticket_created INTEGER NOT NULL DEFAULT 0`,
|
||||
`ALTER TABLE users ADD COLUMN notif_ticket_assigned INTEGER NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE users ADD COLUMN notif_new_comment INTEGER NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE users ADD COLUMN notif_weekly_report INTEGER NOT NULL DEFAULT 1`,
|
||||
`ALTER TABLE assets ADD COLUMN department TEXT NOT NULL DEFAULT 'IT'`,
|
||||
`UPDATE roles SET name = 'produktion', description = 'Produktion - Sieht und verwaltet Produktions-Assets' WHERE name = 'techniker'`,
|
||||
`ALTER TABLE assets ADD COLUMN inventory_number TEXT`,
|
||||
`ALTER TABLE assets ADD COLUMN purchase_price REAL`,
|
||||
`ALTER TABLE assets ADD COLUMN useful_life_years INTEGER`,
|
||||
`ALTER TABLE assets ADD COLUMN residual_value REAL DEFAULT 0`,
|
||||
`CREATE TABLE IF NOT EXISTS network_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'host' CHECK(type IN ('host','switch','router','printer','nas','service')),
|
||||
host TEXT NOT NULL,
|
||||
check_type TEXT NOT NULL DEFAULT 'icmp' CHECK(check_type IN ('icmp','http','https','tcp')),
|
||||
port INTEGER,
|
||||
http_path TEXT DEFAULT '/',
|
||||
http_keyword TEXT,
|
||||
interval_sec INTEGER NOT NULL DEFAULT 60,
|
||||
timeout_sec INTEGER NOT NULL DEFAULT 5,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
notify_email TEXT,
|
||||
location TEXT,
|
||||
last_status TEXT DEFAULT 'unknown',
|
||||
last_checked DATETIME,
|
||||
last_rtt_ms REAL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_network_devices_enabled ON network_devices(enabled)`,
|
||||
`CREATE TABLE IF NOT EXISTS device_checks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
device_id INTEGER NOT NULL,
|
||||
status TEXT NOT NULL CHECK(status IN ('up','down')),
|
||||
rtt_ms REAL,
|
||||
error_msg TEXT,
|
||||
checked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (device_id) REFERENCES network_devices(id) ON DELETE CASCADE
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_device_checks_time ON device_checks(device_id, checked_at)`,
|
||||
`CREATE TABLE IF NOT EXISTS teams_channels (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
team_id TEXT NOT NULL,
|
||||
team_name TEXT,
|
||||
channel_id TEXT NOT NULL UNIQUE,
|
||||
channel_name TEXT,
|
||||
created_at_teams TEXT,
|
||||
first_seen_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
notified INTEGER NOT NULL DEFAULT 0
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_teams_channels_notified ON teams_channels(notified)`,
|
||||
`CREATE TABLE IF NOT EXISTS unifi_config (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||
controller_url TEXT NOT NULL DEFAULT '',
|
||||
username TEXT NOT NULL DEFAULT '',
|
||||
password TEXT NOT NULL DEFAULT '',
|
||||
site TEXT NOT NULL DEFAULT 'default',
|
||||
poll_interval_min INTEGER NOT NULL DEFAULT 5,
|
||||
enabled INTEGER NOT NULL DEFAULT 0
|
||||
)`,
|
||||
`INSERT OR IGNORE INTO unifi_config (id) VALUES (1)`,
|
||||
`CREATE TABLE IF NOT EXISTS unifi_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
unifi_id TEXT UNIQUE,
|
||||
mac TEXT,
|
||||
name TEXT,
|
||||
ip TEXT,
|
||||
type TEXT,
|
||||
model TEXT,
|
||||
version TEXT,
|
||||
state INTEGER DEFAULT 0,
|
||||
uptime INTEGER DEFAULT 0,
|
||||
num_sta INTEGER DEFAULT 0,
|
||||
cpu_pct REAL,
|
||||
ram_pct REAL,
|
||||
ports_json TEXT DEFAULT '[]',
|
||||
radio_json TEXT DEFAULT '[]',
|
||||
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_unifi_devices_type ON unifi_devices(type)`,
|
||||
`CREATE TABLE IF NOT EXISTS external_alerts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
source TEXT DEFAULT 'netgo',
|
||||
device TEXT,
|
||||
service TEXT,
|
||||
state_transition TEXT,
|
||||
severity TEXT,
|
||||
message TEXT,
|
||||
customer TEXT,
|
||||
monitored_by TEXT,
|
||||
state_time TEXT,
|
||||
raw_body TEXT,
|
||||
acknowledged INTEGER DEFAULT 0,
|
||||
ticket_id INTEGER,
|
||||
email_message_id TEXT UNIQUE,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_external_alerts_acknowledged ON external_alerts(acknowledged)`,
|
||||
`ALTER TABLE tickets ADD COLUMN satisfaction_comment TEXT`,
|
||||
`ALTER TABLE external_alerts ADD COLUMN ai_analysis TEXT`,
|
||||
// === LAGER-MODUL MIGRATIONS ===
|
||||
`ALTER TABLE assets ADD COLUMN location_id INTEGER REFERENCES warehouse_locations(id) ON DELETE SET NULL`,
|
||||
`ALTER TABLE assets ADD COLUMN min_stock INTEGER DEFAULT 0`,
|
||||
`CREATE TABLE IF NOT EXISTS warehouse_locations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`INSERT OR IGNORE INTO warehouse_locations (name, description) VALUES ('Büro', 'Allgemeines Büro')`,
|
||||
`INSERT OR IGNORE INTO warehouse_locations (name, description) VALUES ('Serverraum', 'Serverraum / Technikraum')`,
|
||||
`INSERT OR IGNORE INTO warehouse_locations (name, description) VALUES ('Lager', 'Hauptlager')`,
|
||||
`CREATE TABLE IF NOT EXISTS asset_movements (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
asset_id INTEGER NOT NULL,
|
||||
type TEXT NOT NULL CHECK(type IN ('in','out','transfer','status_change')),
|
||||
from_location_id INTEGER REFERENCES warehouse_locations(id) ON DELETE SET NULL,
|
||||
to_location_id INTEGER REFERENCES warehouse_locations(id) ON DELETE SET NULL,
|
||||
assigned_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
ticket_id INTEGER REFERENCES tickets(id) ON DELETE SET NULL,
|
||||
reason TEXT,
|
||||
notes TEXT,
|
||||
performed_by INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE CASCADE
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_asset_movements_asset ON asset_movements(asset_id)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_asset_movements_created ON asset_movements(created_at)`,
|
||||
`CREATE TABLE IF NOT EXISTS stock_thresholds (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category TEXT NOT NULL UNIQUE,
|
||||
min_stock INTEGER NOT NULL DEFAULT 1,
|
||||
notify_email TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS purchase_orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category TEXT NOT NULL,
|
||||
item_name TEXT NOT NULL,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
status TEXT NOT NULL DEFAULT 'offen' CHECK(status IN ('offen','bestellt','erledigt')),
|
||||
notes TEXT,
|
||||
created_by INTEGER NOT NULL REFERENCES users(id),
|
||||
ordered_at DATETIME,
|
||||
completed_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_purchase_orders_status ON purchase_orders(status)`,
|
||||
`CREATE TABLE IF NOT EXISTS asset_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
icon TEXT DEFAULT 'devices_other',
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Notebook', 'laptop', 1)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Monitor', 'monitor', 2)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Headset', 'headset', 3)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Drucker', 'print', 4)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Smartphone', 'smartphone', 5)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Tablet', 'tablet', 6)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Server', 'storage', 7)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Switch', 'device_hub', 8)`,
|
||||
`INSERT OR IGNORE INTO asset_types (name, icon, sort_order) VALUES ('Sonstiges', 'devices_other', 99)`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN agent_version TEXT DEFAULT '1.0.0'`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN tpm_present INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN tpm_version TEXT`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN tpm_v2 INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN secure_boot INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN win11_ready INTEGER DEFAULT 0`,
|
||||
`DROP TABLE IF EXISTS patch_commands_v2`,
|
||||
`CREATE TABLE patch_commands_v2 (id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id INTEGER NOT NULL, command TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','sent','done','failed')), triggered_by_user_id INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, sent_at DATETIME, completed_at DATETIME, result TEXT)`,
|
||||
`INSERT OR IGNORE INTO patch_commands_v2 SELECT id,agent_id,command,status,triggered_by_user_id,created_at,sent_at,completed_at,result FROM patch_commands`,
|
||||
`DROP TABLE patch_commands`,
|
||||
`ALTER TABLE patch_commands_v2 RENAME TO patch_commands`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_patch_commands_agent ON patch_commands(agent_id, status)`,
|
||||
`DROP TABLE IF EXISTS patch_commands_v3`,
|
||||
`CREATE TABLE patch_commands_v3 (id INTEGER PRIMARY KEY AUTOINCREMENT, agent_id INTEGER NOT NULL, command TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','sent','done','failed','running')), triggered_by_user_id INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, sent_at DATETIME, completed_at DATETIME, result TEXT)`,
|
||||
`INSERT OR IGNORE INTO patch_commands_v3 SELECT id,agent_id,command,status,triggered_by_user_id,created_at,sent_at,completed_at,result FROM patch_commands`,
|
||||
`DROP TABLE patch_commands`,
|
||||
`ALTER TABLE patch_commands_v3 RENAME TO patch_commands`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_patch_commands_agent_v3 ON patch_commands(agent_id, status)`,
|
||||
`ALTER TABLE portal_guides ADD COLUMN visible_roles TEXT DEFAULT '[]'`,
|
||||
`CREATE TABLE IF NOT EXISTS patch_groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
color TEXT DEFAULT '#3B82F6',
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`INSERT OR IGNORE INTO patch_groups (name, description, color, sort_order) VALUES ('Test', 'Testgeräte – zuerst patchen', '#8B5CF6', 1)`,
|
||||
`INSERT OR IGNORE INTO patch_groups (name, description, color, sort_order) VALUES ('Pilot', 'Pilotgruppe – frühe Nutzer', '#F59E0B', 2)`,
|
||||
`INSERT OR IGNORE INTO patch_groups (name, description, color, sort_order) VALUES ('Produktion', 'Produktionsgeräte – zuletzt patchen', '#10B981', 3)`,
|
||||
`CREATE TABLE IF NOT EXISTS patch_policies (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
group_id INTEGER NOT NULL REFERENCES patch_groups(id) ON DELETE CASCADE,
|
||||
severity TEXT NOT NULL DEFAULT 'critical' CHECK(severity IN ('critical','important','moderate','low','all')),
|
||||
max_days INTEGER NOT NULL DEFAULT 7,
|
||||
notify_email TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS patch_agent_groups (
|
||||
agent_id INTEGER NOT NULL REFERENCES monitoring_agents(id) ON DELETE CASCADE,
|
||||
group_id INTEGER NOT NULL REFERENCES patch_groups(id) ON DELETE CASCADE,
|
||||
assigned_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (agent_id, group_id)
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS patch_commands (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
agent_id INTEGER NOT NULL REFERENCES monitoring_agents(id) ON DELETE CASCADE,
|
||||
command TEXT NOT NULL CHECK(command IN ('install_updates','check_updates','reboot')),
|
||||
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','sent','done','failed')),
|
||||
triggered_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
sent_at DATETIME,
|
||||
completed_at DATETIME,
|
||||
result TEXT
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_patch_commands_agent ON patch_commands(agent_id, status)`,
|
||||
`ALTER TABLE patch_groups ADD COLUMN target_agent_version TEXT DEFAULT NULL`,
|
||||
`CREATE TABLE IF NOT EXISTS ai_knowledge (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'Allgemein',
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
created_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`DROP TABLE IF EXISTS announcement_acks`,
|
||||
`DROP TABLE IF EXISTS announcements`,
|
||||
`CREATE TABLE IF NOT EXISTS announcements (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'info' CHECK(type IN ('info','warning','maintenance')),
|
||||
target_groups TEXT NOT NULL DEFAULT '[]',
|
||||
created_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at DATETIME DEFAULT NULL,
|
||||
active INTEGER NOT NULL DEFAULT 1
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS announcement_acks (
|
||||
announcement_id INTEGER NOT NULL REFERENCES announcements(id) ON DELETE CASCADE,
|
||||
agent_id INTEGER NOT NULL REFERENCES monitoring_agents(id) ON DELETE CASCADE,
|
||||
hostname TEXT NOT NULL,
|
||||
acknowledged_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (announcement_id, agent_id)
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS health_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
checked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
api_status TEXT NOT NULL DEFAULT 'ok',
|
||||
db_status TEXT NOT NULL DEFAULT 'ok',
|
||||
response_ms INTEGER
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_health_history_date ON health_history(checked_at)`,
|
||||
`CREATE TABLE IF NOT EXISTS portal_guides (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'Allgemein',
|
||||
description TEXT,
|
||||
html_content TEXT NOT NULL,
|
||||
icon TEXT DEFAULT '📄',
|
||||
sort_order INTEGER DEFAULT 0,
|
||||
is_public INTEGER DEFAULT 1,
|
||||
created_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
// Agent v2.0.0 — neue Felder
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN bitlocker_status TEXT DEFAULT 'unknown'`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN defender_enabled INTEGER DEFAULT 0`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN defender_signatures_age INTEGER DEFAULT -1`,
|
||||
`ALTER TABLE monitoring_agents ADD COLUMN hardware_serial TEXT`,
|
||||
// Sichere Links (Shares)
|
||||
`CREATE TABLE IF NOT EXISTS shares (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token TEXT UNIQUE NOT NULL,
|
||||
type TEXT NOT NULL CHECK(type IN ('file','text')),
|
||||
filename TEXT,
|
||||
stored_filename TEXT,
|
||||
text_content TEXT,
|
||||
password_hash TEXT,
|
||||
expires_at DATETIME,
|
||||
max_downloads INTEGER,
|
||||
download_count INTEGER DEFAULT 0,
|
||||
created_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at DATETIME
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS idx_shares_token ON shares(token)`,
|
||||
// Benutzerverwaltung — erweiterte User-Felder
|
||||
`ALTER TABLE users ADD COLUMN department TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN position TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN phone TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN location TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN manager_id INTEGER REFERENCES users(id)`,
|
||||
`ALTER TABLE users ADD COLUMN cost_center TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN employment_type TEXT`,
|
||||
`ALTER TABLE users ADD COLUMN work_hours INTEGER`,
|
||||
`ALTER TABLE users ADD COLUMN joined_date DATE`,
|
||||
// Benutzerverwaltung — erweiterte FIDO-Key-Felder
|
||||
`ALTER TABLE fido_keys ADD COLUMN key_type TEXT DEFAULT 'primary'`,
|
||||
`ALTER TABLE fido_keys ADD COLUMN last_used_at DATETIME`,
|
||||
`ALTER TABLE fido_keys ADD COLUMN manufacturer TEXT`,
|
||||
`ALTER TABLE fido_keys ADD COLUMN connection_type TEXT`,
|
||||
// Asset-Agent-Sync
|
||||
`ALTER TABLE assets ADD COLUMN os TEXT`,
|
||||
`ALTER TABLE assets ADD COLUMN ip_address TEXT`,
|
||||
`ALTER TABLE assets ADD COLUMN manufacturer TEXT`,
|
||||
`ALTER TABLE assets ADD COLUMN last_agent_sync DATETIME`,
|
||||
`ALTER TABLE announcements ADD COLUMN target_agent_ids TEXT NOT NULL DEFAULT '[]'`,
|
||||
`CREATE TABLE IF NOT EXISTS security_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
filename TEXT NOT NULL,
|
||||
original_filename TEXT NOT NULL,
|
||||
report_period TEXT,
|
||||
risk_level TEXT DEFAULT 'unbekannt',
|
||||
summary_json TEXT,
|
||||
created_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
];
|
||||
for (const migration of migrations) {
|
||||
try {
|
||||
db.exec(migration);
|
||||
} catch (e) {
|
||||
// Column already exists – ignore
|
||||
}
|
||||
}
|
||||
console.log('✅ Database migrations completed');
|
||||
|
||||
// Special migration: rebuild network_devices to add SNMP + AP support
|
||||
try {
|
||||
const ndDef = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='network_devices'").get();
|
||||
if (ndDef && !ndDef.sql.includes('snmp_community')) {
|
||||
console.log('🔄 Migrating network_devices (adding SNMP + AP support)...');
|
||||
db.pragma('foreign_keys = OFF');
|
||||
db.exec(`CREATE TABLE network_devices_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'host' CHECK(type IN ('host','switch','router','printer','nas','service','ap')),
|
||||
host TEXT NOT NULL,
|
||||
check_type TEXT NOT NULL DEFAULT 'icmp' CHECK(check_type IN ('icmp','http','https','tcp','snmp')),
|
||||
port INTEGER,
|
||||
http_path TEXT DEFAULT '/',
|
||||
http_keyword TEXT,
|
||||
snmp_community TEXT DEFAULT 'public',
|
||||
snmp_version TEXT DEFAULT '2c',
|
||||
interval_sec INTEGER NOT NULL DEFAULT 60,
|
||||
timeout_sec INTEGER NOT NULL DEFAULT 5,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
notify_email TEXT,
|
||||
location TEXT,
|
||||
last_status TEXT DEFAULT 'unknown',
|
||||
last_checked DATETIME,
|
||||
last_rtt_ms REAL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
db.exec(`INSERT INTO network_devices_new
|
||||
(id, name, type, host, check_type, port, http_path, http_keyword,
|
||||
snmp_community, snmp_version, interval_sec, timeout_sec, enabled,
|
||||
notify_email, location, last_status, last_checked, last_rtt_ms, created_at)
|
||||
SELECT id, name, type, host, check_type, port, http_path, http_keyword,
|
||||
'public', '2c', interval_sec, timeout_sec, enabled,
|
||||
notify_email, location, last_status, last_checked, last_rtt_ms, created_at
|
||||
FROM network_devices`);
|
||||
db.exec('DROP TABLE network_devices');
|
||||
db.exec('ALTER TABLE network_devices_new RENAME TO network_devices');
|
||||
db.pragma('foreign_keys = ON');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_network_devices_enabled ON network_devices(enabled)');
|
||||
console.log('✅ network_devices SNMP migration complete');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('❌ network_devices SNMP migration failed:', e.message);
|
||||
}
|
||||
|
||||
// Special migration: add personal data columns to onboarding_protocols
|
||||
try {
|
||||
const obDef = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='onboarding_protocols'").get();
|
||||
if (obDef && !obDef.sql.includes('emp_first_name')) {
|
||||
console.log('🔄 Migrating onboarding_protocols (adding personal data columns)...');
|
||||
db.pragma('foreign_keys = OFF');
|
||||
db.exec(`CREATE TABLE onboarding_protocols_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
employee_user_id INTEGER,
|
||||
emp_first_name TEXT,
|
||||
emp_last_name TEXT,
|
||||
emp_private_email TEXT,
|
||||
emp_phone TEXT,
|
||||
emp_address TEXT,
|
||||
department TEXT,
|
||||
position TEXT,
|
||||
work_location TEXT,
|
||||
hours_model TEXT,
|
||||
vacation_model TEXT,
|
||||
status VARCHAR(20) NOT NULL CHECK(status IN ('pending', 'in_progress', 'completed')),
|
||||
start_date DATE NOT NULL,
|
||||
completion_date DATE,
|
||||
checklist_data TEXT,
|
||||
notes TEXT,
|
||||
pdf_file_path VARCHAR(500),
|
||||
created_by_user_id INTEGER NOT NULL,
|
||||
updated_by_user_id INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (employee_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (updated_by_user_id) REFERENCES users(id)
|
||||
)`);
|
||||
db.exec(`INSERT INTO onboarding_protocols_new
|
||||
(id, employee_user_id, status, start_date, completion_date, checklist_data, notes, pdf_file_path, created_by_user_id, updated_by_user_id, created_at, updated_at)
|
||||
SELECT id, employee_user_id, status, start_date, completion_date, checklist_data, notes, pdf_file_path, created_by_user_id, updated_by_user_id, created_at, updated_at
|
||||
FROM onboarding_protocols`);
|
||||
db.exec('DROP TABLE onboarding_protocols');
|
||||
db.exec('ALTER TABLE onboarding_protocols_new RENAME TO onboarding_protocols');
|
||||
db.pragma('foreign_keys = ON');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_onboarding_employee ON onboarding_protocols(employee_user_id)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_onboarding_status ON onboarding_protocols(status)');
|
||||
console.log('✅ Onboarding personal data migration complete');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('❌ Onboarding migration failed:', e.message);
|
||||
}
|
||||
|
||||
// Special migration: update ticket status CHECK constraint to add response-based statuses
|
||||
try {
|
||||
const ticketDef = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='tickets'").get();
|
||||
if (ticketDef && !ticketDef.sql.includes('warten_auf_mitarbeiter')) {
|
||||
console.log('🔄 Migrating ticket status constraint (adding warten_auf_mitarbeiter/support)...');
|
||||
db.pragma('foreign_keys = OFF');
|
||||
db.exec(`CREATE TABLE tickets_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_number TEXT UNIQUE NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'offen'
|
||||
CHECK(status IN ('offen','in_bearbeitung','warten_auf_mitarbeiter','warten_auf_support','geschlossen')),
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'mittel'
|
||||
CHECK(priority IN ('niedrig','mittel','hoch','kritisch')),
|
||||
category VARCHAR(50) NOT NULL DEFAULT 'Allgemein'
|
||||
CHECK(category IN ('Software','Allgemein','SelectLine','Hardware')),
|
||||
source VARCHAR(10) NOT NULL DEFAULT 'web'
|
||||
CHECK(source IN ('web','email')),
|
||||
requester_name TEXT,
|
||||
requester_email TEXT,
|
||||
assigned_to_user_id INTEGER,
|
||||
created_by_user_id INTEGER,
|
||||
asset_id INTEGER,
|
||||
email_message_id TEXT,
|
||||
snoozed_until DATETIME,
|
||||
ai_suggestion TEXT,
|
||||
ai_active INTEGER DEFAULT 1,
|
||||
resolved_at DATETIME,
|
||||
closed_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (assigned_to_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (asset_id) REFERENCES assets(id) ON DELETE SET NULL
|
||||
)`);
|
||||
db.exec(`INSERT INTO tickets_new
|
||||
SELECT id, ticket_number, title, description,
|
||||
CASE status WHEN 'geloest' THEN 'in_bearbeitung' ELSE status END,
|
||||
priority, category, source, requester_name, requester_email,
|
||||
assigned_to_user_id, created_by_user_id, asset_id, email_message_id,
|
||||
snoozed_until, ai_suggestion, ai_active, resolved_at, closed_at,
|
||||
created_at, updated_at
|
||||
FROM tickets`);
|
||||
db.exec('DROP TABLE tickets');
|
||||
db.exec('ALTER TABLE tickets_new RENAME TO tickets');
|
||||
db.pragma('foreign_keys = ON');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_priority ON tickets(priority)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_assigned ON tickets(assigned_to_user_id)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_created_by ON tickets(created_by_user_id)');
|
||||
console.log('✅ Ticket status migration complete');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('❌ Ticket status migration failed:', e.message);
|
||||
}
|
||||
|
||||
// Check if super admin already exists
|
||||
const existingAdmin = db.prepare('SELECT id FROM users WHERE username = ?').get(DEFAULT_SUPERADMIN.username);
|
||||
|
||||
if (!existingAdmin) {
|
||||
console.log('👤 Creating default Super Admin user...');
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await bcrypt.hash(DEFAULT_SUPERADMIN.password, BCRYPT_ROUNDS);
|
||||
|
||||
// Get super_admin role ID
|
||||
const superAdminRole = db.prepare('SELECT id FROM roles WHERE name = ?').get('super_admin');
|
||||
|
||||
// Insert super admin user
|
||||
const insertUser = db.prepare(`
|
||||
INSERT INTO users (
|
||||
username,
|
||||
email,
|
||||
password_hash,
|
||||
role_id,
|
||||
first_name,
|
||||
last_name,
|
||||
is_active,
|
||||
must_change_password
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
insertUser.run(
|
||||
DEFAULT_SUPERADMIN.username,
|
||||
DEFAULT_SUPERADMIN.email,
|
||||
passwordHash,
|
||||
superAdminRole.id,
|
||||
DEFAULT_SUPERADMIN.first_name,
|
||||
DEFAULT_SUPERADMIN.last_name,
|
||||
1, // is_active
|
||||
1 // must_change_password
|
||||
);
|
||||
|
||||
console.log('✅ Super Admin user created successfully');
|
||||
console.log('');
|
||||
console.log('═══════════════════════════════════════════');
|
||||
console.log('🔐 DEFAULT SUPER ADMIN CREDENTIALS');
|
||||
console.log('═══════════════════════════════════════════');
|
||||
console.log(`Username: ${DEFAULT_SUPERADMIN.username}`);
|
||||
console.log(`Password: ${DEFAULT_SUPERADMIN.password}`);
|
||||
console.log('');
|
||||
console.log('⚠️ IMPORTANT: Please change this password on first login!');
|
||||
console.log('═══════════════════════════════════════════');
|
||||
console.log('');
|
||||
} else {
|
||||
console.log('ℹ️ Super Admin user already exists, skipping creation');
|
||||
}
|
||||
|
||||
db.close();
|
||||
console.log('✅ Database initialization completed successfully!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Database initialization failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
initializeDatabase();
|
||||
}
|
||||
|
||||
module.exports = { initializeDatabase };
|
||||
Reference in New Issue
Block a user