-- Local authentication: password reset tokens + per-IP attempt throttling. -- -- Added when fitme3070 stopped delegating login to the Andmore ID hub and went -- back to owning its own passwords (users.password_hash, which was never -- dropped). See config/functions_user.php and config/functions_throttle.php. -- -- Safe to re-run: every statement is IF NOT EXISTS. CREATE TABLE IF NOT EXISTS `password_resets` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL, `token` VARCHAR(64) NOT NULL, `expires_at` DATETIME NOT NULL, `used_at` DATETIME DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uq_pr_token` (`token`), KEY `idx_pr_user` (`user_id`), CONSTRAINT `fk_pr_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS `auth_attempts` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `ip` VARCHAR(45) NOT NULL, `scope` VARCHAR(20) NOT NULL, `attempted_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_aa_window` (`ip`, `scope`, `attempted_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- `users.andmore_id` is intentionally NOT dropped. It no longer has any code -- reading or writing it, but it records which central Andmore ID each row was -- linked to during the SSO era — useful if those accounts ever need to be -- reconciled. Drop it by hand once that history stops mattering.