-- Admin users and sessions for the Next.js admin area. -- Run after catherine_league schema exists: mysql ... < admin_auth.sql USE `catherine_league`; -- Passwords stored as bcrypt hashes (e.g. from bcryptjs), never plaintext. CREATE TABLE IF NOT EXISTS `admin_users` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `email` VARCHAR(255) NOT NULL, `password_hash` VARCHAR(255) NOT NULL, `is_approved` TINYINT(1) NOT NULL DEFAULT 0, `is_admin` TINYINT(1) NOT NULL DEFAULT 0, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_admin_users_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Opaque session tokens: store SHA-256 hex of the cookie value; never store raw tokens. CREATE TABLE IF NOT EXISTS `admin_sessions` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL, `token_hash` CHAR(64) NOT NULL, `expires_at` DATETIME NOT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_admin_sessions_token_hash` (`token_hash`), KEY `idx_admin_sessions_user_id` (`user_id`), KEY `idx_admin_sessions_expires_at` (`expires_at`), CONSTRAINT `fk_admin_sessions_user` FOREIGN KEY (`user_id`) REFERENCES `admin_users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Seed: first admin (change password immediately in production). -- Default password: ChangeMeOnFirstLogin! INSERT INTO `admin_users` (`email`, `password_hash`, `is_approved`, `is_admin`) SELECT 'admin@localhost', '$2b$12$H6Y71zX/nmefp33e0MaMaOOBGSiVwxVE3L.Ie3Pfq1/6QZdLR7bTa', 1, 1 WHERE NOT EXISTS (SELECT 1 FROM `admin_users` WHERE `email` = 'admin@localhost' LIMIT 1);