-- Rosary Presenter -- MySQL 8.x / MariaDB schema -- Canonical, consolidated schema (structure only). Supersedes the old -- install.php + migrate_v2..v6.php chain of hand-run scripts. -- -- This file is DDL only -- every CREATE TABLE is IF NOT EXISTS, so it's safe to -- run directly (mysql -u user -p dbname < schema.sql) against either an empty -- database or an existing one. Seed data (site_settings defaults, the -- superadmin account, and the standard prayer library) is NOT included here: -- it's inserted by install.php via PHP arrays + prepared statements instead of -- raw SQL, both because the superadmin password needs PHP's password_hash() -- and to avoid any risk of the prayer texts' own punctuation (they contain -- semicolons and apostrophes) being misread as SQL syntax. -- -- For an EXISTING production install that already ran the old migrate_v2..v6 -- scripts, applying this file is a no-op EXCEPT for the new -- users.failed_login_attempts / users.locked_until columns added for login -- rate-limiting, and the sessions/novena_groups.photo_focal_x/photo_focal_y/ -- photo_zoom columns added for the photo reposition tool -- see README.md -- "Upgrading an Existing Install" for the manual ALTER TABLE statements -- needed there. CREATE TABLE IF NOT EXISTS sessions ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NULL, is_public TINYINT(1) NOT NULL DEFAULT 1, slug VARCHAR(255) NULL, name VARCHAR(255) NOT NULL, occasion VARCHAR(50) NOT NULL, mystery_set VARCHAR(50) NOT NULL, novena_day TINYINT NULL, novena_group_id INT NULL, subject_name VARCHAR(255) NULL, subject_pronoun VARCHAR(10) NULL, subject_dates VARCHAR(150) NULL, photo_path VARCHAR(500) NULL, photo_focal_x FLOAT NOT NULL DEFAULT 50, photo_focal_y FLOAT NOT NULL DEFAULT 50, photo_zoom FLOAT NOT NULL DEFAULT 1, is_pinned TINYINT(1) NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS novena_groups ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NULL, is_public TINYINT(1) NOT NULL DEFAULT 1, slug VARCHAR(255) NULL, name VARCHAR(255) NOT NULL, mystery_set VARCHAR(50) NOT NULL DEFAULT 'sorrowful', subject_name VARCHAR(255) NULL, subject_pronoun VARCHAR(10) NULL, subject_dates VARCHAR(150) NULL, photo_path VARCHAR(500) NULL, photo_focal_x FLOAT NOT NULL DEFAULT 50, photo_focal_y FLOAT NOT NULL DEFAULT 50, photo_zoom FLOAT NOT NULL DEFAULT 1, is_pinned TINYINT(1) NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, display_name VARCHAR(100) NULL, role ENUM('superadmin','admin','superuser','user') NOT NULL DEFAULT 'user', rosary_limit INT NOT NULL DEFAULT 1, email_confirmed TINYINT(1) NOT NULL DEFAULT 0, confirm_token VARCHAR(64) NULL, reset_token VARCHAR(64) NULL, reset_expires DATETIME NULL, failed_login_attempts INT NOT NULL DEFAULT 0, locked_until DATETIME NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS site_settings ( key_name VARCHAR(100) PRIMARY KEY, val TEXT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS custom_prayers ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, leader_text TEXT, all_text TEXT, default_bead_type ENUM('small','large','crucifix') NULL, is_global TINYINT(1) NOT NULL DEFAULT 0, created_by INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_global (is_global), INDEX idx_created_by (created_by) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS builder_steps ( id INT AUTO_INCREMENT PRIMARY KEY, session_id INT NOT NULL, step_type ENUM('prayer','bead') NOT NULL DEFAULT 'prayer', bead_type ENUM('small','large','crucifix') NULL, step_order INT NOT NULL DEFAULT 0, prayer_id INT NULL, attribution ENUM('leader_all','leader_only','all_only','none') NOT NULL DEFAULT 'leader_all', INDEX idx_session (session_id), FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;