174 lines
6.1 KiB
Python
174 lines
6.1 KiB
Python
# -----------------------------------------------------------------------------
|
|
# Description: SQLite database initialization and persistence helpers for users, projects, cells, and project metadata.
|
|
# Inside functions: connect_db, init_db, get_user, get_user_profile, get_user_auth_by_id, update_user_occupation, update_user_password, add_user_log, list_user_logs
|
|
# Developer : Qin Yue @ 2026
|
|
# Organization : OptiHK Limited
|
|
# -----------------------------------------------------------------------------
|
|
# backend/database.py
|
|
import sqlite3
|
|
import os
|
|
from werkzeug.security import generate_password_hash
|
|
from datetime import datetime
|
|
|
|
# Save the database in the backend folder
|
|
DB_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "database", "mxpic_data.db"))
|
|
|
|
def connect_db():
|
|
conn = sqlite3.connect(DB_FILE, timeout=20)
|
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
conn.execute("PRAGMA busy_timeout=20000")
|
|
return conn
|
|
|
|
def init_db():
|
|
os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
|
|
# Create Users Table
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL
|
|
)
|
|
''')
|
|
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS user_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
username TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
project TEXT,
|
|
cell TEXT,
|
|
detail TEXT,
|
|
ip_address TEXT,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY(user_id) REFERENCES users(id)
|
|
)
|
|
''')
|
|
|
|
cursor.execute("PRAGMA table_info(users)")
|
|
existing_columns = {row[1] for row in cursor.fetchall()}
|
|
migrations = {
|
|
"created_at": "ALTER TABLE users ADD COLUMN created_at TEXT",
|
|
"credits": "ALTER TABLE users ADD COLUMN credits INTEGER NOT NULL DEFAULT 0",
|
|
"occupation": "ALTER TABLE users ADD COLUMN occupation TEXT NOT NULL DEFAULT 'intern'",
|
|
"user_group": "ALTER TABLE users ADD COLUMN user_group TEXT NOT NULL DEFAULT 'user'",
|
|
}
|
|
for column, statement in migrations.items():
|
|
if column not in existing_columns:
|
|
cursor.execute(statement)
|
|
|
|
now = datetime.utcnow().strftime("%Y-%m-%d")
|
|
cursor.execute("UPDATE users SET created_at = ? WHERE created_at IS NULL OR created_at = ''", (now,))
|
|
cursor.execute("UPDATE users SET user_group = 'manager' WHERE username = 'admin'")
|
|
cursor.execute("UPDATE users SET user_group = 'developers' WHERE username = 'engineer'")
|
|
cursor.execute("UPDATE users SET user_group = 'user' WHERE user_group IS NULL OR user_group = ''")
|
|
|
|
# Insert default users for local multi-account development.
|
|
cursor.execute("SELECT * FROM users WHERE username = 'admin'")
|
|
if not cursor.fetchone():
|
|
test_hash = generate_password_hash("123456")
|
|
cursor.execute(
|
|
"INSERT INTO users (username, password_hash, created_at, credits, occupation, user_group) VALUES (?, ?, ?, ?, ?, ?)",
|
|
("admin", test_hash, now, 0, "principle engineer", "manager")
|
|
)
|
|
print("Test user created. Username: admin | Password: 123456")
|
|
|
|
cursor.execute("SELECT * FROM users WHERE username = 'engineer'")
|
|
if not cursor.fetchone():
|
|
test_hash = generate_password_hash("123456")
|
|
cursor.execute(
|
|
"INSERT INTO users (username, password_hash, created_at, credits, occupation, user_group) VALUES (?, ?, ?, ?, ?, ?)",
|
|
("engineer", test_hash, now, 0, "junior engineer", "developers")
|
|
)
|
|
print("Second test user created. Username: engineer | Password: 123456")
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def get_user(username):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute("SELECT id, username, password_hash, user_group FROM users WHERE username = ?", (username,))
|
|
user = cursor.fetchone()
|
|
conn.close()
|
|
return user
|
|
|
|
def get_user_profile(user_id):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
"SELECT id, username, created_at, credits, occupation, user_group FROM users WHERE id = ?",
|
|
(user_id,)
|
|
)
|
|
user = cursor.fetchone()
|
|
conn.close()
|
|
return user
|
|
|
|
def get_user_auth_by_id(user_id):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute("SELECT id, username, password_hash FROM users WHERE id = ?", (user_id,))
|
|
user = cursor.fetchone()
|
|
conn.close()
|
|
return user
|
|
|
|
def update_user_occupation(user_id, occupation):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute("UPDATE users SET occupation = ? WHERE id = ?", (occupation, user_id))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def update_user_password(user_id, password):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute("UPDATE users SET password_hash = ? WHERE id = ?", (generate_password_hash(password), user_id))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def add_user_log(user_id, username, action, project=None, cell=None, detail=None, ip_address=None):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
'''
|
|
INSERT INTO user_logs (user_id, username, action, project, cell, detail, ip_address, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
''',
|
|
(
|
|
user_id,
|
|
username,
|
|
action,
|
|
project,
|
|
cell,
|
|
detail,
|
|
ip_address,
|
|
datetime.utcnow().isoformat(timespec="seconds") + "Z"
|
|
)
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def list_user_logs(user_id, limit=200):
|
|
conn = connect_db()
|
|
cursor = conn.cursor()
|
|
cursor.execute(
|
|
'''
|
|
SELECT id, action, project, cell, detail, ip_address, created_at
|
|
FROM user_logs
|
|
WHERE user_id = ?
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
''',
|
|
(user_id, limit)
|
|
)
|
|
rows = cursor.fetchall()
|
|
conn.close()
|
|
return rows
|
|
|
|
if __name__ == "__main__":
|
|
init_db()
|
|
print("Database initialized successfully.")
|