Initial commit
Dieser Commit ist enthalten in:
536
backend/database.js
Normale Datei
536
backend/database.js
Normale Datei
@ -0,0 +1,536 @@
|
||||
/**
|
||||
* TASKMATE - Datenbank
|
||||
* ====================
|
||||
* SQLite-Datenbank mit better-sqlite3
|
||||
*/
|
||||
|
||||
const Database = require('better-sqlite3');
|
||||
const path = require('path');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const logger = require('./utils/logger');
|
||||
|
||||
// Datenbank-Pfad
|
||||
const DB_PATH = process.env.DB_PATH || path.join(__dirname, 'data', 'taskmate.db');
|
||||
|
||||
let db = null;
|
||||
|
||||
/**
|
||||
* Datenbank initialisieren
|
||||
*/
|
||||
async function initialize() {
|
||||
try {
|
||||
// Datenbank öffnen/erstellen
|
||||
db = new Database(DB_PATH);
|
||||
|
||||
// WAL-Modus für bessere Performance
|
||||
db.pragma('journal_mode = WAL');
|
||||
db.pragma('foreign_keys = ON');
|
||||
|
||||
// Tabellen erstellen
|
||||
createTables();
|
||||
|
||||
// Standard-Benutzer erstellen (falls nicht vorhanden)
|
||||
await createDefaultUsers();
|
||||
|
||||
logger.info('Datenbank initialisiert');
|
||||
return db;
|
||||
} catch (error) {
|
||||
logger.error('Datenbank-Fehler:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tabellen erstellen
|
||||
*/
|
||||
function createTables() {
|
||||
// Benutzer
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
color TEXT NOT NULL DEFAULT '#00D4FF',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login DATETIME,
|
||||
failed_attempts INTEGER DEFAULT 0,
|
||||
locked_until DATETIME
|
||||
)
|
||||
`);
|
||||
|
||||
// Login-Audit
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS login_audit (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
ip_address TEXT,
|
||||
success INTEGER NOT NULL,
|
||||
user_agent TEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Projekte
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS projects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
archived INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Spalten
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS columns (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
color TEXT,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Labels
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS labels (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
color TEXT NOT NULL,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Aufgaben
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
column_id INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
priority TEXT DEFAULT 'medium',
|
||||
start_date DATE,
|
||||
due_date DATE,
|
||||
assigned_to INTEGER,
|
||||
time_estimate_min INTEGER,
|
||||
depends_on INTEGER,
|
||||
position INTEGER NOT NULL,
|
||||
archived INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (column_id) REFERENCES columns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (assigned_to) REFERENCES users(id),
|
||||
FOREIGN KEY (depends_on) REFERENCES tasks(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Migration: Add start_date column if it doesn't exist
|
||||
const columns = db.prepare("PRAGMA table_info(tasks)").all();
|
||||
const hasStartDate = columns.some(col => col.name === 'start_date');
|
||||
if (!hasStartDate) {
|
||||
db.exec('ALTER TABLE tasks ADD COLUMN start_date DATE');
|
||||
logger.info('Migration: start_date Spalte zu tasks hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add role and permissions columns to users
|
||||
const userColumns = db.prepare("PRAGMA table_info(users)").all();
|
||||
const hasRole = userColumns.some(col => col.name === 'role');
|
||||
if (!hasRole) {
|
||||
db.exec("ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user'");
|
||||
logger.info('Migration: role Spalte zu users hinzugefuegt');
|
||||
}
|
||||
const hasPermissions = userColumns.some(col => col.name === 'permissions');
|
||||
if (!hasPermissions) {
|
||||
db.exec("ALTER TABLE users ADD COLUMN permissions TEXT DEFAULT '[]'");
|
||||
logger.info('Migration: permissions Spalte zu users hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add email column to users
|
||||
const hasEmail = userColumns.some(col => col.name === 'email');
|
||||
if (!hasEmail) {
|
||||
db.exec("ALTER TABLE users ADD COLUMN email TEXT");
|
||||
logger.info('Migration: email Spalte zu users hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add repositories_base_path column to users
|
||||
const hasRepoBasePath = userColumns.some(col => col.name === 'repositories_base_path');
|
||||
if (!hasRepoBasePath) {
|
||||
db.exec("ALTER TABLE users ADD COLUMN repositories_base_path TEXT");
|
||||
logger.info('Migration: repositories_base_path Spalte zu users hinzugefuegt');
|
||||
}
|
||||
|
||||
// Proposals (Vorschlaege)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS proposals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_by INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
approved INTEGER DEFAULT 0,
|
||||
approved_by INTEGER,
|
||||
approved_at DATETIME,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id),
|
||||
FOREIGN KEY (approved_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Proposal Votes
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS proposal_votes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
proposal_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Unique constraint for proposal votes (one vote per user per proposal)
|
||||
db.exec(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_proposal_votes_unique
|
||||
ON proposal_votes(proposal_id, user_id)
|
||||
`);
|
||||
|
||||
// Index for proposal votes
|
||||
db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_proposal_votes_proposal
|
||||
ON proposal_votes(proposal_id)
|
||||
`);
|
||||
|
||||
// Migration: Add archived, task_id, and project_id columns to proposals
|
||||
const proposalColumns = db.prepare("PRAGMA table_info(proposals)").all();
|
||||
const hasProposalArchived = proposalColumns.some(col => col.name === 'archived');
|
||||
if (!hasProposalArchived) {
|
||||
db.exec('ALTER TABLE proposals ADD COLUMN archived INTEGER DEFAULT 0');
|
||||
logger.info('Migration: archived Spalte zu proposals hinzugefuegt');
|
||||
}
|
||||
const hasProposalTaskId = proposalColumns.some(col => col.name === 'task_id');
|
||||
if (!hasProposalTaskId) {
|
||||
db.exec('ALTER TABLE proposals ADD COLUMN task_id INTEGER REFERENCES tasks(id) ON DELETE SET NULL');
|
||||
logger.info('Migration: task_id Spalte zu proposals hinzugefuegt');
|
||||
}
|
||||
const hasProposalProjectId = proposalColumns.some(col => col.name === 'project_id');
|
||||
if (!hasProposalProjectId) {
|
||||
db.exec('ALTER TABLE proposals ADD COLUMN project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE');
|
||||
logger.info('Migration: project_id Spalte zu proposals hinzugefuegt');
|
||||
}
|
||||
|
||||
// Migration: Add filter_category column to columns
|
||||
const columnColumns = db.prepare("PRAGMA table_info(columns)").all();
|
||||
const hasFilterCategory = columnColumns.some(col => col.name === 'filter_category');
|
||||
if (!hasFilterCategory) {
|
||||
db.exec("ALTER TABLE columns ADD COLUMN filter_category TEXT DEFAULT 'in_progress'");
|
||||
logger.info('Migration: filter_category Spalte zu columns hinzugefuegt');
|
||||
|
||||
// Set default values for existing columns based on position
|
||||
const projects = db.prepare('SELECT id FROM projects').all();
|
||||
for (const project of projects) {
|
||||
const cols = db.prepare('SELECT id, position FROM columns WHERE project_id = ? ORDER BY position').all(project.id);
|
||||
if (cols.length > 0) {
|
||||
// First column = open
|
||||
db.prepare("UPDATE columns SET filter_category = 'open' WHERE id = ?").run(cols[0].id);
|
||||
// Last column = completed
|
||||
if (cols.length > 1) {
|
||||
db.prepare("UPDATE columns SET filter_category = 'completed' WHERE id = ?").run(cols[cols.length - 1].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info('Migration: Standard-Filterkategorien fuer bestehende Spalten gesetzt');
|
||||
}
|
||||
|
||||
// Task-Labels (Verknüpfung)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS task_labels (
|
||||
task_id INTEGER NOT NULL,
|
||||
label_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (task_id, label_id),
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Task-Assignees (Mehrfachzuweisung von Mitarbeitern)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS task_assignees (
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (task_id, user_id),
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Unteraufgaben
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS subtasks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
completed INTEGER DEFAULT 0,
|
||||
position INTEGER NOT NULL,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Kommentare
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Anhänge
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS attachments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
original_name TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
size_bytes INTEGER NOT NULL,
|
||||
uploaded_by INTEGER,
|
||||
uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (uploaded_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Links
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
title TEXT,
|
||||
url TEXT NOT NULL,
|
||||
created_by INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Aufgaben-Vorlagen
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS task_templates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
title_template TEXT,
|
||||
description TEXT,
|
||||
priority TEXT,
|
||||
labels TEXT,
|
||||
subtasks TEXT,
|
||||
time_estimate_min INTEGER,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Historie
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
task_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
field_changed TEXT,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Einstellungen
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
`);
|
||||
|
||||
// Anwendungen (Git-Repositories pro Projekt)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS applications (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_id INTEGER NOT NULL UNIQUE,
|
||||
local_path TEXT NOT NULL,
|
||||
gitea_repo_url TEXT,
|
||||
gitea_repo_owner TEXT,
|
||||
gitea_repo_name TEXT,
|
||||
default_branch TEXT DEFAULT 'main',
|
||||
last_sync DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by INTEGER,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Benachrichtigungen (Inbox)
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
message TEXT,
|
||||
task_id INTEGER,
|
||||
project_id INTEGER,
|
||||
proposal_id INTEGER,
|
||||
actor_id INTEGER,
|
||||
is_read INTEGER DEFAULT 0,
|
||||
is_persistent INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (proposal_id) REFERENCES proposals(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// Indizes für Performance
|
||||
db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_column ON tasks(column_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to);
|
||||
CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_subtasks_task ON subtasks(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_comments_task ON comments(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_history_task ON history(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_attachments_task ON attachments(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_links_task ON links(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_labels_task ON task_labels(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_labels_label ON task_labels(label_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_notifications_user_read ON notifications(user_id, is_read);
|
||||
CREATE INDEX IF NOT EXISTS idx_notifications_created ON notifications(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_applications_project ON applications(project_id);
|
||||
`);
|
||||
|
||||
logger.info('Datenbank-Tabellen erstellt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard-Benutzer erstellen
|
||||
*/
|
||||
async function createDefaultUsers() {
|
||||
const existingUsers = db.prepare('SELECT COUNT(*) as count FROM users').get();
|
||||
|
||||
if (existingUsers.count === 0) {
|
||||
// Benutzer aus Umgebungsvariablen
|
||||
const user1 = {
|
||||
username: process.env.USER1_USERNAME || 'user1',
|
||||
password: process.env.USER1_PASSWORD || 'changeme123',
|
||||
displayName: process.env.USER1_DISPLAYNAME || 'Benutzer 1',
|
||||
color: process.env.USER1_COLOR || '#00D4FF'
|
||||
};
|
||||
|
||||
const user2 = {
|
||||
username: process.env.USER2_USERNAME || 'user2',
|
||||
password: process.env.USER2_PASSWORD || 'changeme456',
|
||||
displayName: process.env.USER2_DISPLAYNAME || 'Benutzer 2',
|
||||
color: process.env.USER2_COLOR || '#FF9500'
|
||||
};
|
||||
|
||||
const insertUser = db.prepare(`
|
||||
INSERT INTO users (username, password_hash, display_name, color, role, permissions)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
// Admin-Benutzer
|
||||
const adminUser = {
|
||||
username: 'admin',
|
||||
password: '!1Data123',
|
||||
displayName: 'Administrator',
|
||||
color: '#8B5CF6'
|
||||
};
|
||||
|
||||
// Passwoerter hashen und Benutzer erstellen
|
||||
const hash1 = await bcrypt.hash(user1.password, 12);
|
||||
const hash2 = await bcrypt.hash(user2.password, 12);
|
||||
const hashAdmin = await bcrypt.hash(adminUser.password, 12);
|
||||
|
||||
insertUser.run(user1.username, hash1, user1.displayName, user1.color, 'user', '[]');
|
||||
insertUser.run(user2.username, hash2, user2.displayName, user2.color, 'user', '[]');
|
||||
insertUser.run(adminUser.username, hashAdmin, adminUser.displayName, adminUser.color, 'admin', '[]');
|
||||
|
||||
logger.info('Standard-Benutzer und Admin erstellt');
|
||||
|
||||
// Standard-Projekt erstellen
|
||||
const projectResult = db.prepare(`
|
||||
INSERT INTO projects (name, description, created_by)
|
||||
VALUES (?, ?, ?)
|
||||
`).run('Mein erstes Projekt', 'Willkommen bei TaskMate!', 1);
|
||||
|
||||
const projectId = projectResult.lastInsertRowid;
|
||||
|
||||
// Standard-Spalten erstellen
|
||||
const insertColumn = db.prepare(`
|
||||
INSERT INTO columns (project_id, name, position, color)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
insertColumn.run(projectId, 'Offen', 0, null);
|
||||
insertColumn.run(projectId, 'In Arbeit', 1, null);
|
||||
insertColumn.run(projectId, 'Erledigt', 2, null);
|
||||
|
||||
// Standard-Labels erstellen
|
||||
const insertLabel = db.prepare(`
|
||||
INSERT INTO labels (project_id, name, color)
|
||||
VALUES (?, ?, ?)
|
||||
`);
|
||||
|
||||
insertLabel.run(projectId, 'Bug', '#DC2626');
|
||||
insertLabel.run(projectId, 'Feature', '#059669');
|
||||
insertLabel.run(projectId, 'Dokumentation', '#3182CE');
|
||||
|
||||
logger.info('Standard-Projekt mit Spalten und Labels erstellt');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Datenbank-Instanz abrufen
|
||||
*/
|
||||
function getDb() {
|
||||
if (!db) {
|
||||
throw new Error('Datenbank nicht initialisiert');
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Datenbank schließen
|
||||
*/
|
||||
function close() {
|
||||
if (db) {
|
||||
db.close();
|
||||
logger.info('Datenbank geschlossen');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initialize,
|
||||
getDb,
|
||||
close
|
||||
};
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren