Files
panel/src/lib/init-db.js

386 lines
11 KiB
JavaScript

import db from "./db.js";
export default function initializeDatabase() {
db.exec(`
-- Table: contracts
CREATE TABLE IF NOT EXISTS contracts (
contract_id INTEGER PRIMARY KEY AUTOINCREMENT,
contract_number TEXT NOT NULL,
contract_name TEXT,
customer_contract_number TEXT,
customer TEXT,
investor TEXT,
date_signed TEXT,
finish_date TEXT
);
-- Table: projects
CREATE TABLE IF NOT EXISTS projects (
project_id INTEGER PRIMARY KEY AUTOINCREMENT,
contract_id INTEGER,
project_name TEXT NOT NULL,
project_number TEXT NOT NULL,
address TEXT,
plot TEXT,
district TEXT,
unit TEXT,
city TEXT,
investment_number TEXT,
finish_date TEXT,
wp TEXT,
contact TEXT,
notes TEXT,
project_type TEXT CHECK(project_type IN ('design', 'construction', 'design+construction')) DEFAULT 'design',
project_status TEXT CHECK(project_status IN ('registered', 'in_progress_design', 'in_progress_construction', 'fulfilled', 'cancelled')) DEFAULT 'registered',
FOREIGN KEY (contract_id) REFERENCES contracts(contract_id)
);
-- Table: tasks
CREATE TABLE IF NOT EXISTS tasks (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
max_wait_days INTEGER DEFAULT 0,
is_standard INTEGER NOT NULL DEFAULT 0
); -- Table: project_tasks
CREATE TABLE IF NOT EXISTS project_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER NOT NULL,
task_template_id INTEGER,
custom_task_name TEXT,
custom_max_wait_days INTEGER DEFAULT 0,
status TEXT NOT NULL DEFAULT 'pending',
date_added TEXT DEFAULT CURRENT_TIMESTAMP,
priority TEXT DEFAULT 'normal',
FOREIGN KEY (project_id) REFERENCES projects(project_id),
FOREIGN KEY (task_template_id) REFERENCES tasks(task_id)
);
-- Table: notes
CREATE TABLE IF NOT EXISTS notes (
note_id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER,
task_id INTEGER,
note TEXT NOT NULL,
note_date TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects(project_id),
FOREIGN KEY (task_id) REFERENCES project_tasks(id)
);
`);
// Migration: Add custom task columns if they don't exist
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN custom_task_name TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN custom_max_wait_days INTEGER DEFAULT 0;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add description column to tasks table
try {
db.exec(`
ALTER TABLE tasks ADD COLUMN description TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add description column to project_tasks table
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN custom_description TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add project_type column to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN project_type TEXT CHECK(project_type IN ('design', 'construction', 'design+construction')) DEFAULT 'design';
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add project_status column to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN project_status TEXT CHECK(project_status IN ('registered', 'in_progress_design', 'in_progress_construction', 'fulfilled', 'cancelled')) DEFAULT 'registered';
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add coordinates column to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN coordinates TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Copy data from geo_info to coordinates and drop geo_info
try {
db.exec(`
UPDATE projects SET coordinates = geo_info WHERE geo_info IS NOT NULL;
`);
db.exec(`
ALTER TABLE projects DROP COLUMN geo_info;
`);
} catch (e) {
// Column migration already done or geo_info doesn't exist, ignore error
}
// Migration: Add date_started column to project_tasks table
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN date_started TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add is_system column to notes table
try {
db.exec(`
ALTER TABLE notes ADD COLUMN is_system INTEGER DEFAULT 0;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add date_completed column to project_tasks table
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN date_completed TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add user tracking columns to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN created_by TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE projects ADD COLUMN assigned_to TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE projects ADD COLUMN created_at TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE projects ADD COLUMN updated_at TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add user tracking columns to project_tasks table
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN created_by TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN assigned_to TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN created_at TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE project_tasks ADD COLUMN updated_at TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
// Create indexes for project_tasks user tracking
try {
db.exec(`
CREATE INDEX IF NOT EXISTS idx_project_tasks_created_by ON project_tasks(created_by);
CREATE INDEX IF NOT EXISTS idx_project_tasks_assigned_to ON project_tasks(assigned_to);
`);
} catch (e) {
// Index already exists, ignore error
}
// Migration: Add user tracking columns to notes table
try {
db.exec(`
ALTER TABLE notes ADD COLUMN created_by TEXT;
`);
} catch (e) {
// Column already exists, ignore error
}
try {
db.exec(`
ALTER TABLE notes ADD COLUMN is_system INTEGER DEFAULT 0;
`);
} catch (e) {
// Column already exists, ignore error
}
// Create indexes for notes user tracking
try {
db.exec(`
CREATE INDEX IF NOT EXISTS idx_notes_created_by ON notes(created_by);
CREATE INDEX IF NOT EXISTS idx_notes_project_id ON notes(project_id);
CREATE INDEX IF NOT EXISTS idx_notes_task_id ON notes(task_id);
`);
} catch (e) {
// Index already exists, ignore error
}
// Authorization tables
db.exec(`
-- Users table
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
name TEXT NOT NULL,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT CHECK(role IN ('admin', 'project_manager', 'user', 'read_only')) DEFAULT 'user',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_active INTEGER DEFAULT 1,
last_login TEXT,
failed_login_attempts INTEGER DEFAULT 0,
locked_until TEXT
);
-- NextAuth.js sessions table (simplified for custom implementation)
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
session_token TEXT UNIQUE NOT NULL,
user_id TEXT NOT NULL,
expires TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Audit log table for security tracking
CREATE TABLE IF NOT EXISTS audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT,
action TEXT NOT NULL,
resource_type TEXT,
resource_id TEXT,
ip_address TEXT,
user_agent TEXT,
timestamp TEXT DEFAULT CURRENT_TIMESTAMP,
details TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
CREATE INDEX IF NOT EXISTS idx_sessions_token ON sessions(session_token);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_user_timestamp ON audit_logs(user_id, timestamp);
`);
// Migration: Add username column and migrate from email if needed
try {
// Check if username column exists
const columns = db.prepare("PRAGMA table_info(users)").all();
const hasUsername = columns.some(col => col.name === 'username');
const hasEmail = columns.some(col => col.name === 'email');
if (!hasUsername && hasEmail) {
// Add username column
db.exec(`ALTER TABLE users ADD COLUMN username TEXT;`);
// Migrate existing email data to username (for development/testing)
// In production, you might want to handle this differently
db.exec(`UPDATE users SET username = email WHERE username IS NULL;`);
// Create unique index on username
db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username_unique ON users(username);`);
console.log("✅ Migrated users table from email to username");
} else if (!hasUsername) {
// If neither username nor email exists, something is wrong
console.warn("⚠️ Users table missing both username and email columns");
}
} catch (e) {
console.warn("Migration warning:", e.message);
}
// Generic file attachments table
db.exec(`
CREATE TABLE IF NOT EXISTS file_attachments (
file_id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL CHECK(entity_type IN ('contract', 'project', 'task')),
entity_id INTEGER NOT NULL,
original_filename TEXT NOT NULL,
stored_filename TEXT NOT NULL,
file_path TEXT NOT NULL,
file_size INTEGER,
mime_type TEXT,
description TEXT,
uploaded_by TEXT,
upload_date TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (uploaded_by) REFERENCES users(id)
);
-- Create indexes for file attachments
CREATE INDEX IF NOT EXISTS idx_file_attachments_entity ON file_attachments(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_file_attachments_uploaded_by ON file_attachments(uploaded_by);
-- Generic field change history table
CREATE TABLE IF NOT EXISTS field_change_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
table_name TEXT NOT NULL,
record_id INTEGER NOT NULL,
field_name TEXT NOT NULL,
old_value TEXT,
new_value TEXT,
changed_by INTEGER,
changed_at TEXT DEFAULT CURRENT_TIMESTAMP,
change_reason TEXT,
FOREIGN KEY (changed_by) REFERENCES users(id)
);
-- Create indexes for field change history
CREATE INDEX IF NOT EXISTS idx_field_history_table_record ON field_change_history(table_name, record_id);
CREATE INDEX IF NOT EXISTS idx_field_history_field ON field_change_history(table_name, record_id, field_name);
CREATE INDEX IF NOT EXISTS idx_field_history_changed_by ON field_change_history(changed_by);
`);
}