This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
|
||||
def _table_columns(engine: Engine, table_name: str) -> set[str]:
|
||||
with engine.connect() as conn:
|
||||
rows = conn.execute(text(f"PRAGMA table_info({table_name})")).mappings().all()
|
||||
return {row["name"] for row in rows}
|
||||
|
||||
|
||||
def run_startup_migrations(engine: Engine) -> None:
|
||||
if engine.dialect.name != "sqlite":
|
||||
return
|
||||
|
||||
user_columns = _table_columns(engine, "users")
|
||||
|
||||
statements: list[str] = []
|
||||
if "preferred_home_view" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN preferred_home_view VARCHAR(16) NOT NULL DEFAULT 'week'")
|
||||
if "preferred_month_view_mode" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN preferred_month_view_mode VARCHAR(16) NOT NULL DEFAULT 'flat'")
|
||||
if "entry_mode" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN entry_mode VARCHAR(16) NOT NULL DEFAULT 'manual'")
|
||||
if "working_days_csv" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN working_days_csv VARCHAR(32) NOT NULL DEFAULT '0,1,2,3,4'")
|
||||
if "count_vacation_as_worktime" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN count_vacation_as_worktime BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "count_holiday_as_worktime" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN count_holiday_as_worktime BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "count_sick_as_worktime" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN count_sick_as_worktime BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "automatic_break_rules_enabled" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN automatic_break_rules_enabled BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "default_break_minutes" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN default_break_minutes INTEGER NOT NULL DEFAULT 0")
|
||||
if "overtime_start_date" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN overtime_start_date DATE")
|
||||
if "overtime_expiry_days" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN overtime_expiry_days INTEGER")
|
||||
if "expire_negative_overtime" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN expire_negative_overtime BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "vacation_days_total" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN vacation_days_total INTEGER NOT NULL DEFAULT 0")
|
||||
if "vacation_show_in_header" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN vacation_show_in_header BOOLEAN NOT NULL DEFAULT 1")
|
||||
if "workhours_counter_enabled" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_enabled BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "workhours_counter_show_in_header" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_show_in_header BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "workhours_counter_start_date" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_start_date DATE")
|
||||
if "workhours_counter_end_date" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_end_date DATE")
|
||||
if "workhours_counter_manual_offset_minutes" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_manual_offset_minutes INTEGER NOT NULL DEFAULT 0")
|
||||
if "workhours_counter_target_minutes" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_target_minutes INTEGER")
|
||||
if "workhours_counter_target_email_enabled" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_target_email_enabled BOOLEAN NOT NULL DEFAULT 0")
|
||||
if "workhours_counter_warning_last_sent_on" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_warning_last_sent_on DATE")
|
||||
if "workhours_counter_warning_last_sent_key" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN workhours_counter_warning_last_sent_key VARCHAR(120)")
|
||||
if "federal_state" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN federal_state VARCHAR(8)")
|
||||
if "email_verified" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN email_verified BOOLEAN NOT NULL DEFAULT 1")
|
||||
if "email_verification_token_hash" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN email_verification_token_hash VARCHAR(128)")
|
||||
if "email_verification_expires_at" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN email_verification_expires_at DATETIME")
|
||||
if "email_verification_sent_at" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN email_verification_sent_at DATETIME")
|
||||
if "mfa_method" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN mfa_method VARCHAR(16) NOT NULL DEFAULT 'none'")
|
||||
if "mfa_totp_secret_encrypted" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN mfa_totp_secret_encrypted TEXT")
|
||||
if "mfa_email_code_hash" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN mfa_email_code_hash VARCHAR(255)")
|
||||
if "mfa_email_code_expires_at" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN mfa_email_code_expires_at DATETIME")
|
||||
if "mfa_email_code_sent_at" not in user_columns:
|
||||
statements.append("ALTER TABLE users ADD COLUMN mfa_email_code_sent_at DATETIME")
|
||||
|
||||
email_config_columns = _table_columns(engine, "email_server_config")
|
||||
if "registration_admin_notify_enabled" not in email_config_columns:
|
||||
statements.append("ALTER TABLE email_server_config ADD COLUMN registration_admin_notify_enabled BOOLEAN NOT NULL DEFAULT 1")
|
||||
if "registration_admin_notify_admin_ids_csv" not in email_config_columns:
|
||||
statements.append("ALTER TABLE email_server_config ADD COLUMN registration_admin_notify_admin_ids_csv VARCHAR(1024)")
|
||||
|
||||
time_entry_columns = _table_columns(engine, "time_entries")
|
||||
if "break_rule_mode" not in time_entry_columns:
|
||||
statements.append("ALTER TABLE time_entries ADD COLUMN break_rule_mode VARCHAR(16) NOT NULL DEFAULT 'manual'")
|
||||
|
||||
if not statements:
|
||||
return
|
||||
|
||||
with engine.begin() as conn:
|
||||
for statement in statements:
|
||||
conn.execute(text(statement))
|
||||
conn.execute(text("UPDATE users SET entry_mode = 'auto_until_today' WHERE entry_mode = 'auto'"))
|
||||
conn.execute(
|
||||
text("CREATE INDEX IF NOT EXISTS ix_users_email_verification_token_hash ON users (email_verification_token_hash)")
|
||||
)
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS overtime_adjustments (
|
||||
id VARCHAR(36) PRIMARY KEY NOT NULL,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
minutes INTEGER NOT NULL,
|
||||
notes TEXT,
|
||||
created_at DATETIME NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_user_overtime_adjustment_date UNIQUE (user_id, date)
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_overtime_adjustments_user_id ON overtime_adjustments (user_id)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_overtime_adjustments_date ON overtime_adjustments (date)"))
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS auto_entry_skips (
|
||||
id VARCHAR(36) PRIMARY KEY NOT NULL,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
FOREIGN KEY(user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_user_auto_entry_skip_date UNIQUE (user_id, date)
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_auto_entry_skips_user_id ON auto_entry_skips (user_id)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_auto_entry_skips_date ON auto_entry_skips (date)"))
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS site_content (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
key VARCHAR(64) NOT NULL UNIQUE,
|
||||
markdown_text TEXT NOT NULL DEFAULT '',
|
||||
updated_by_user_id VARCHAR(36),
|
||||
updated_at DATETIME,
|
||||
FOREIGN KEY(updated_by_user_id) REFERENCES users (id) ON DELETE SET NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_site_content_key ON site_content (key)"))
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS support_tickets (
|
||||
id VARCHAR(36) PRIMARY KEY NOT NULL,
|
||||
user_id VARCHAR(36),
|
||||
category VARCHAR(24) NOT NULL DEFAULT 'problem',
|
||||
status VARCHAR(24) NOT NULL DEFAULT 'open',
|
||||
name VARCHAR(255) NOT NULL DEFAULT '',
|
||||
email VARCHAR(255) NOT NULL,
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
admin_notes TEXT,
|
||||
source_ip_hash VARCHAR(128),
|
||||
source_user_agent VARCHAR(512),
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME,
|
||||
closed_at DATETIME,
|
||||
FOREIGN KEY(user_id) REFERENCES users (id) ON DELETE SET NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_support_tickets_user_id ON support_tickets (user_id)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_support_tickets_email ON support_tickets (email)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_support_tickets_status ON support_tickets (status)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_support_tickets_source_ip_hash ON support_tickets (source_ip_hash)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS ix_support_tickets_created_at ON support_tickets (created_at)"))
|
||||
Reference in New Issue
Block a user