diff --git a/scripts/migrations/000_base.sql b/scripts/migrations/000_base.sql index b2af060..11f9358 100644 --- a/scripts/migrations/000_base.sql +++ b/scripts/migrations/000_base.sql @@ -158,4 +158,32 @@ ALTER TABLE capex ALTER TABLE opex DROP COLUMN IF EXISTS currency_code; +-- Role-based access control tables +CREATE TABLE IF NOT EXISTS roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) UNIQUE NOT NULL +); + +CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + username VARCHAR(255) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + hashed_password VARCHAR(255) NOT NULL, + role_id INTEGER NOT NULL REFERENCES roles (id) ON DELETE RESTRICT +); + +CREATE INDEX IF NOT EXISTS ix_users_username ON users (username); +CREATE INDEX IF NOT EXISTS ix_users_email ON users (email); + +-- Theme settings configuration table +CREATE TABLE IF NOT EXISTS theme_settings ( + id SERIAL PRIMARY KEY, + theme_name VARCHAR(255) UNIQUE NOT NULL, + primary_color VARCHAR(7) NOT NULL, + secondary_color VARCHAR(7) NOT NULL, + accent_color VARCHAR(7) NOT NULL, + background_color VARCHAR(7) NOT NULL, + text_color VARCHAR(7) NOT NULL +); + COMMIT; diff --git a/scripts/migrations/20251025_create_application_setting_table.sql b/scripts/migrations/20251025_create_application_setting_table.sql deleted file mode 100644 index 380a14a..0000000 --- a/scripts/migrations/20251025_create_application_setting_table.sql +++ /dev/null @@ -1,25 +0,0 @@ --- Migration: Create application_setting table for configurable application options --- Date: 2025-10-25 --- Description: Introduces persistent storage for application-level settings such as theme colors. - -BEGIN; - -CREATE TABLE IF NOT EXISTS application_setting ( - id SERIAL PRIMARY KEY, - key VARCHAR(128) NOT NULL UNIQUE, - value TEXT NOT NULL, - value_type VARCHAR(32) NOT NULL DEFAULT 'string', - category VARCHAR(32) NOT NULL DEFAULT 'general', - description TEXT, - is_editable BOOLEAN NOT NULL DEFAULT TRUE, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); - -CREATE UNIQUE INDEX IF NOT EXISTS ux_application_setting_key - ON application_setting (key); - -CREATE INDEX IF NOT EXISTS ix_application_setting_category - ON application_setting (category); - -COMMIT; diff --git a/scripts/migrations/20251027_create_theme_settings_table.sql b/scripts/migrations/20251027_create_theme_settings_table.sql deleted file mode 100644 index 8e2b448..0000000 --- a/scripts/migrations/20251027_create_theme_settings_table.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Migration: 20251027_create_theme_settings_table.sql - -CREATE TABLE theme_settings ( - id SERIAL PRIMARY KEY, - theme_name VARCHAR(255) UNIQUE NOT NULL, - primary_color VARCHAR(7) NOT NULL, - secondary_color VARCHAR(7) NOT NULL, - accent_color VARCHAR(7) NOT NULL, - background_color VARCHAR(7) NOT NULL, - text_color VARCHAR(7) NOT NULL -); diff --git a/scripts/migrations/20251027_create_user_and_role_tables.sql b/scripts/migrations/20251027_create_user_and_role_tables.sql deleted file mode 100644 index 5ae47b2..0000000 --- a/scripts/migrations/20251027_create_user_and_role_tables.sql +++ /dev/null @@ -1,15 +0,0 @@ --- Migration: 20251027_create_user_and_role_tables.sql - -CREATE TABLE roles ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) UNIQUE NOT NULL -); - -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - username VARCHAR(255) UNIQUE NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - hashed_password VARCHAR(255) NOT NULL, - role_id INTEGER NOT NULL, - FOREIGN KEY (role_id) REFERENCES roles(id) -); diff --git a/scripts/setup_database.py b/scripts/setup_database.py index 3c51eb3..5d09fb8 100644 --- a/scripts/setup_database.py +++ b/scripts/setup_database.py @@ -22,6 +22,7 @@ connection string; this script will still honor the granular inputs above. """ from __future__ import annotations +from config.database import Base import argparse import importlib import logging @@ -43,7 +44,6 @@ from sqlalchemy import create_engine, inspect ROOT_DIR = Path(__file__).resolve().parents[1] if str(ROOT_DIR) not in sys.path: sys.path.insert(0, str(ROOT_DIR)) -from config.database import Base logger = logging.getLogger(__name__) @@ -126,7 +126,8 @@ class DatabaseConfig: ] if missing: raise RuntimeError( - "Missing required database configuration: " + ", ".join(missing) + "Missing required database configuration: " + + ", ".join(missing) ) host = cast(str, host) @@ -340,7 +341,8 @@ class DatabaseSetup: rollback_label = f"drop database {self.config.database}" self._register_rollback( rollback_label, - lambda db=self.config.database: self._drop_database(db), + lambda db=self.config.database: self._drop_database( + db), ) logger.info("Created database '%s'", self.config.database) finally: @@ -409,7 +411,8 @@ class DatabaseSetup: rollback_label = f"drop role {self.config.user}" self._register_rollback( rollback_label, - lambda role=self.config.user: self._drop_role(role), + lambda role=self.config.user: self._drop_role( + role), ) else: logger.info("Role '%s' already present", self.config.user) @@ -839,6 +842,7 @@ class DatabaseSetup: seed_args = argparse.Namespace( currencies=True, units=True, + theme=True, defaults=False, dry_run=dry_run, verbose=0, diff --git a/tests/unit/test_setup_database.py b/tests/unit/test_setup_database.py index 4432d16..efea513 100644 --- a/tests/unit/test_setup_database.py +++ b/tests/unit/test_setup_database.py @@ -46,6 +46,7 @@ def test_seed_baseline_data_dry_run_skips_verification( assert namespace_arg.dry_run is True assert namespace_arg.currencies is True assert namespace_arg.units is True + assert namespace_arg.theme is True assert seed_run.call_args.kwargs["config"] is setup_instance.config verify_mock.assert_not_called() @@ -67,6 +68,7 @@ def test_seed_baseline_data_invokes_verification( assert isinstance(namespace_arg, argparse.Namespace) assert namespace_arg.dry_run is False assert seed_run.call_args.kwargs["config"] is setup_instance.config + assert namespace_arg.theme is True verify_mock.assert_called_once_with( expected_currency_codes=expected_currencies, expected_unit_codes=expected_units,