185 lines
6.0 KiB
Python
185 lines
6.0 KiB
Python
import os, hashlib, sqlite3, logging
|
|
from os import getenv as env
|
|
import mysql.connector
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
FETCHALL = 0
|
|
FETCHONE = 1
|
|
|
|
|
|
class Database:
|
|
def __init__(self):
|
|
self.connection = None
|
|
self.cursor = None
|
|
|
|
def connect_sqlite(self, db_path):
|
|
try:
|
|
self.connection = sqlite3.connect(db_path, check_same_thread=False)
|
|
self.cursor = self.connection.cursor()
|
|
log.info("Connected to SQLite database")
|
|
except sqlite3.Error as e:
|
|
log.error(f"SQLite connection error: {e}")
|
|
raise
|
|
|
|
def connect_mysql(self):
|
|
try:
|
|
self.connection = mysql.connector.connect(
|
|
host=env('MYSQL_HOST'),
|
|
user=env('MYSQL_USER'),
|
|
password=env('MYSQL_PASSWORD'),
|
|
database=env('MYSQL_DATABASE')
|
|
)
|
|
self.cursor = self.connection.cursor()
|
|
log.info("Connected to MySQL database")
|
|
except mysql.connector.Error as e:
|
|
log.error(f"MySQL connection error: {e}")
|
|
raise
|
|
|
|
def close(self):
|
|
if self.cursor:
|
|
self.cursor.close()
|
|
if self.connection:
|
|
self.connection.close()
|
|
log.warning("Database connection closed")
|
|
|
|
def execute_query(self, query, params=None, fetch_type=FETCHALL):
|
|
try:
|
|
if params:
|
|
self.cursor.execute(query, params)
|
|
else:
|
|
self.cursor.execute(query)
|
|
self.connection.commit()
|
|
log.debug(f"Executed query: {query}")
|
|
if fetch_type == FETCHALL:
|
|
return self.fetchall()
|
|
elif fetch_type == FETCHONE:
|
|
return self.fetchone()
|
|
elif fetch_type is None:
|
|
log.debug("No fetch type specified, returning None")
|
|
return None
|
|
else:
|
|
log.warning("Invalid fetch type, returning None")
|
|
return None
|
|
except (sqlite3.Error, mysql.connector.Error) as e:
|
|
log.critical(f"Query execution error: {e}")
|
|
raise
|
|
|
|
def fetchall(self):
|
|
try:
|
|
result = self.cursor.fetchall()
|
|
log.debug(f"Fetched all results")
|
|
return result
|
|
except (sqlite3.Error, mysql.connector.Error) as e:
|
|
log.error(f"Fetchall error: {e}")
|
|
raise
|
|
|
|
def fetchone(self):
|
|
try:
|
|
result = self.cursor.fetchone()
|
|
log.debug(f"Fetched one result")
|
|
return result
|
|
except (sqlite3.Error, mysql.connector.Error) as e:
|
|
log.critical(f"Fetchone error: {e}")
|
|
raise
|
|
|
|
|
|
def first_time_run(db:Database):
|
|
log.info("First time run detected, initializing database")
|
|
|
|
# Create users table
|
|
log.info("Creating users table")
|
|
db.execute_query("""
|
|
CREATE TABLE users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
username TEXT NOT NULL,
|
|
password TEXT NOT NULL,
|
|
about TEXT DEFAULT 'No description',
|
|
permissions TEXT DEFAULT 'user',
|
|
avatar MEDIUMBLOB,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
token TEXT,
|
|
avatar_type TEXT
|
|
)
|
|
""")
|
|
|
|
# Create posts table
|
|
log.info("Creating posts table")
|
|
db.execute_query("""
|
|
CREATE TABLE posts (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL,
|
|
board_id INTEGER NOT NULL,
|
|
content TEXT NOT NULL,
|
|
reference INTREGER,
|
|
type TEXT DEFAULT 'post',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
|
FOREIGN KEY (board_id) REFERENCES boards (id) ON DELETE CASCADE,
|
|
FOREIGN KEY (reference) REFERENCES posts (id) ON DELETE SET NULL
|
|
)
|
|
""")
|
|
|
|
# Create boards table
|
|
log.info("Creating boards table")
|
|
db.execute_query("""
|
|
CREATE TABLE boards (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
owner_id INTEGER NOT NULL,
|
|
FOREIGN KEY (owner_id) REFERENCES users (id)
|
|
)
|
|
""")
|
|
|
|
# Create attachments table
|
|
db.execute_query("""
|
|
CREATE TABLE attachments (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
post_id INTEGER NOT NULL,
|
|
file_data MEDIUMBLOB NOT NULL,
|
|
file_name TEXT NOT NULL,
|
|
file_type TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE CASCADE
|
|
)
|
|
""")
|
|
|
|
# Create system user
|
|
|
|
if not env('SYSTEM_PASSWORD'):
|
|
log.warning("SYSTEM_PASSWORD is empty, generating random password")
|
|
password = os.urandom(16).hex()
|
|
log.info(f"Generated system user password: {password}")
|
|
else:
|
|
password = env('SYSTEM_PASSWORD')
|
|
|
|
log.info("Creating system user")
|
|
db.execute_query("""
|
|
INSERT INTO users (username, password, about, permissions)
|
|
VALUES (?, ?, ?, ?)
|
|
""", (
|
|
env('SYSTEM_USER', default='system'),
|
|
env('SYSTEM_PASSWORD', default=hashlib.sha256(password.encode()).hexdigest()),
|
|
env('SYSTEM_ABOUT', default='System User'),
|
|
env('SYSTEM_PERMISSIONS', default='admin')
|
|
))
|
|
|
|
# Create system boards
|
|
boards_names = env('SYSTEM_BOARDS', default='General,Random,System').split(',')
|
|
if "System" not in boards_names:
|
|
boards_names.append("System")
|
|
|
|
log.info(f"Creating system boards: {', '.join(boards_names)}")
|
|
for board_name in boards_names:
|
|
db.execute_query("""
|
|
INSERT INTO boards (name, description, owner_id)
|
|
VALUES (?, ?, ?)
|
|
""", (
|
|
board_name,
|
|
f"This is a automatically created board for {board_name}",
|
|
1
|
|
))
|
|
|
|
log.info("First time run completed") |