chore(db): finish Prisma cutover to Drizzle Kit tooling
Move CMS SQL to drizzle/migrations, drop prisma packages/schema, wire drizzle-kit scripts, and regenerate schema names from src/db/schema.ts. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
d8199ea1e4
commit
ba82789166
47 files changed
+365
-2787
No files matched your search
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
Draft SQL from `pnpm db:generate` (drizzle-kit). Never apply these automatically — copy reviewed statements into drizzle/migrations/ as numbered CMS migrations, then `pnpm db:migrate`.
|
||||
@@ -0,0 +1,10 @@
|
||||
-- CMS-owned settings table. Idempotent: safe to re-run.
|
||||
-- Mirrors AtomCMS's Laravel `website_settings` (key UNIQUE, value TEXT).
|
||||
CREATE TABLE IF NOT EXISTS website_settings (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`key` VARCHAR(255) NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
comment VARCHAR(255) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY website_settings_key_unique (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Fortify-style 2FA columns on the emulator users table. Idempotent (MariaDB).
|
||||
-- AtomCMS installs already have these; this only adds them when missing.
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS two_factor_secret TEXT NULL,
|
||||
ADD COLUMN IF NOT EXISTS two_factor_recovery_codes TEXT NULL,
|
||||
ADD COLUMN IF NOT EXISTS two_factor_confirmed_at TIMESTAMP NULL;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- CMS password reset tokens (Laravel-compatible). Idempotent.
|
||||
CREATE TABLE IF NOT EXISTS password_resets (
|
||||
email VARCHAR(255) NOT NULL,
|
||||
token VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
PRIMARY KEY (email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,440 @@
|
||||
-- 0004_cms_feature_tables.sql
|
||||
-- Idempotent CREATE TABLE for the CMS-owned (website-tier) feature tables
|
||||
-- the AtomCMS-Next conversion needs but the live emulator DB lacks:
|
||||
-- radio_*, game_*/challenges, staff_activities, alert_logs, email_templates,
|
||||
-- social_accounts, website_article_comments/reactions. Generated from
|
||||
-- `prisma migrate diff` (CREATE statements only; all ALTER/DROP excluded so
|
||||
-- emulator-owned tables are never touched). Each statement is IF NOT EXISTS.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_article_reactions` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INTEGER UNSIGNED NOT NULL,
|
||||
`article_id` BIGINT UNSIGNED NOT NULL,
|
||||
`reaction` VARCHAR(50) NOT NULL,
|
||||
`active` BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_article_comments` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`article_id` BIGINT UNSIGNED NOT NULL,
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`comment` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `alert_logs` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`type` VARCHAR(255) NOT NULL,
|
||||
`severity` VARCHAR(255) NOT NULL,
|
||||
`message` TEXT NOT NULL,
|
||||
`context` JSON NULL,
|
||||
`sent_via_email` BOOLEAN NOT NULL DEFAULT false,
|
||||
`sent_via_discord` BOOLEAN NOT NULL DEFAULT false,
|
||||
`is_read` BOOLEAN NOT NULL DEFAULT false,
|
||||
`read_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_ranks` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`description` VARCHAR(255) NULL,
|
||||
`badge_code` VARCHAR(255) NULL,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_shouts` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`message` TEXT NOT NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_applications` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`real_name` VARCHAR(255) NOT NULL,
|
||||
`age` INTEGER NOT NULL,
|
||||
`availability` TEXT NOT NULL,
|
||||
`experience` TEXT NULL,
|
||||
`motivation` TEXT NOT NULL,
|
||||
`music_style` TEXT NULL,
|
||||
`discord_username` VARCHAR(255) NULL,
|
||||
`rank_id` BIGINT UNSIGNED NULL,
|
||||
`status` VARCHAR(255) NOT NULL DEFAULT 'pending',
|
||||
`approved_by` BIGINT UNSIGNED NULL,
|
||||
`approved_at` TIMESTAMP(0) NULL,
|
||||
`rejected_by` BIGINT UNSIGNED NULL,
|
||||
`rejected_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_schedules` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`day` VARCHAR(255) NOT NULL,
|
||||
`start_time` TIME NOT NULL,
|
||||
`end_time` TIME NOT NULL,
|
||||
`show_name` VARCHAR(255) NULL,
|
||||
`description` TEXT NULL,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_banners` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`image_path` VARCHAR(255) NOT NULL,
|
||||
`title` VARCHAR(255) NULL,
|
||||
`description` TEXT NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_history` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`show_name` VARCHAR(255) NULL,
|
||||
`started_at` TIMESTAMP(0) NOT NULL,
|
||||
`ended_at` TIMESTAMP(0) NULL,
|
||||
`listeners_count` INTEGER NOT NULL DEFAULT 0,
|
||||
`notes` TEXT NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_contests` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_giveaways` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_listener_points` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_song_requests` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_song_votes` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_scores` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INTEGER UNSIGNED NOT NULL,
|
||||
`game` VARCHAR(50) NOT NULL,
|
||||
`score` INTEGER NOT NULL DEFAULT 0,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_rooms` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`game` VARCHAR(50) NOT NULL,
|
||||
`player1_id` INTEGER UNSIGNED NOT NULL,
|
||||
`player2_id` INTEGER UNSIGNED NULL,
|
||||
`state` JSON NULL,
|
||||
`turn` INTEGER UNSIGNED NOT NULL DEFAULT 1,
|
||||
`winner_id` INTEGER UNSIGNED NULL,
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'waiting',
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_reward_claims` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`game` VARCHAR(50) NOT NULL,
|
||||
`wins_required` INTEGER NOT NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `game_reward_claims_user_id_game_wins_required_key`(`user_id`, `game`, `wins_required`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_chat_messages` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`room_id` BIGINT UNSIGNED NOT NULL,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`message` TEXT NOT NULL,
|
||||
`type` VARCHAR(191) NOT NULL DEFAULT 'chat',
|
||||
`reactions` JSON NULL,
|
||||
`created_at` TIMESTAMP(0) NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_replays` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`game` VARCHAR(191) NOT NULL,
|
||||
`player1_id` BIGINT UNSIGNED NOT NULL,
|
||||
`player2_id` BIGINT UNSIGNED NULL,
|
||||
`game_data` JSON NOT NULL,
|
||||
`moves` JSON NOT NULL,
|
||||
`winner` VARCHAR(191) NULL,
|
||||
`player1_score` INTEGER NOT NULL DEFAULT 0,
|
||||
`player2_score` INTEGER NOT NULL DEFAULT 0,
|
||||
`started_at` TIMESTAMP(0) NOT NULL,
|
||||
`ended_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_ranks` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(191) NOT NULL,
|
||||
`slug` VARCHAR(191) NOT NULL,
|
||||
`icon` VARCHAR(191) NOT NULL DEFAULT '⭐',
|
||||
`color` VARCHAR(191) NOT NULL DEFAULT '#ffffff',
|
||||
`min_points` INTEGER NOT NULL DEFAULT 0,
|
||||
`max_points` INTEGER NULL,
|
||||
`tier` INTEGER NOT NULL DEFAULT 1,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `game_ranks_slug_key`(`slug`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_game_ranks` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`game` VARCHAR(191) NOT NULL,
|
||||
`rank_id` BIGINT UNSIGNED NOT NULL,
|
||||
`points` INTEGER NOT NULL DEFAULT 0,
|
||||
`wins` INTEGER NOT NULL DEFAULT 0,
|
||||
`games_played` INTEGER NOT NULL DEFAULT 0,
|
||||
`win_streak` INTEGER NOT NULL DEFAULT 0,
|
||||
`best_streak` INTEGER NOT NULL DEFAULT 0,
|
||||
`last_played_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `user_game_ranks_user_id_game_key`(`user_id`, `game`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_invitations` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`from_user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`to_user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`game` VARCHAR(255) NOT NULL,
|
||||
`status` VARCHAR(255) NOT NULL DEFAULT 'pending',
|
||||
`bet_amount` INTEGER NOT NULL DEFAULT 0,
|
||||
`bet_currency` VARCHAR(255) NOT NULL DEFAULT 'credits',
|
||||
`room_id` BIGINT UNSIGNED NULL,
|
||||
`expires_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_game_xp` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`game` VARCHAR(255) NOT NULL,
|
||||
`xp` INTEGER NOT NULL DEFAULT 0,
|
||||
`level` INTEGER NOT NULL DEFAULT 1,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `user_game_xp_user_id_game_key`(`user_id`, `game`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `game_level_rewards` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`game` VARCHAR(255) NOT NULL,
|
||||
`level` INTEGER NOT NULL,
|
||||
`reward_type` VARCHAR(255) NOT NULL,
|
||||
`reward_currency` VARCHAR(255) NULL,
|
||||
`reward_amount` INTEGER NOT NULL DEFAULT 0,
|
||||
`claimed` BOOLEAN NOT NULL DEFAULT false,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `daily_challenges` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`description` VARCHAR(255) NOT NULL,
|
||||
`game` VARCHAR(255) NULL,
|
||||
`type` VARCHAR(255) NOT NULL,
|
||||
`target` INTEGER NOT NULL,
|
||||
`reward_currency` VARCHAR(255) NOT NULL DEFAULT 'credits',
|
||||
`reward_amount` INTEGER NOT NULL,
|
||||
`date` DATE NOT NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_challenge_progress` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`challenge_id` BIGINT UNSIGNED NOT NULL,
|
||||
`progress` INTEGER NOT NULL DEFAULT 0,
|
||||
`completed` BOOLEAN NOT NULL DEFAULT false,
|
||||
`claimed` BOOLEAN NOT NULL DEFAULT false,
|
||||
`completed_at` TIMESTAMP(0) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `user_challenge_progress_user_id_challenge_id_key`(`user_id`, `challenge_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `staff_activities` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`action` VARCHAR(50) NOT NULL,
|
||||
`target_type` VARCHAR(100) NULL,
|
||||
`target_id` BIGINT UNSIGNED NULL,
|
||||
`description` TEXT NOT NULL,
|
||||
`ip_address` VARCHAR(45) NULL,
|
||||
`metadata` JSON NULL,
|
||||
`created_at` TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `email_templates` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`subject` VARCHAR(255) NOT NULL,
|
||||
`body` TEXT NOT NULL,
|
||||
`variables` TEXT NULL,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `email_templates_name_key`(`name`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `social_accounts` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` BIGINT UNSIGNED NOT NULL,
|
||||
`provider` VARCHAR(255) NOT NULL,
|
||||
`provider_id` VARCHAR(255) NOT NULL,
|
||||
`avatar` VARCHAR(255) NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `social_accounts_provider_provider_id_key`(`provider`, `provider_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_api_keys` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`key` VARCHAR(64) NOT NULL,
|
||||
`allowed_ips` VARCHAR(255) NULL,
|
||||
`permissions` JSON NULL,
|
||||
`rate_limit` INTEGER NOT NULL DEFAULT 300,
|
||||
`last_used_at` TIMESTAMP(0) NULL,
|
||||
`expires_at` TIMESTAMP(0) NULL,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
UNIQUE INDEX `radio_api_keys_key_key`(`key`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_song_plays` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`artist` VARCHAR(255) NULL,
|
||||
`album` VARCHAR(255) NULL,
|
||||
`artwork_url` VARCHAR(255) NULL,
|
||||
`duration` INTEGER UNSIGNED NULL,
|
||||
`played_at` TIMESTAMP(0) NOT NULL,
|
||||
`dj_id` BIGINT UNSIGNED NULL,
|
||||
`metadata` JSON NULL,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `radio_auto_dj_playlist` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`artist` VARCHAR(255) NULL,
|
||||
`album` VARCHAR(255) NULL,
|
||||
`artwork_url` VARCHAR(255) NULL,
|
||||
`duration` INTEGER UNSIGNED NULL,
|
||||
`play_count` INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
`last_played_at` TIMESTAMP(0) NULL,
|
||||
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||
`sort_order` INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
`created_at` TIMESTAMP(0) NULL,
|
||||
`updated_at` TIMESTAMP(0) NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- radio_listener_points was created as a stub (id + timestamps only) but the
|
||||
-- /radio/leaderboard query joins users on user_id and orders by points. Add the
|
||||
-- real columns. Idempotent (MariaDB ADD COLUMN / INDEX IF NOT EXISTS).
|
||||
ALTER TABLE radio_listener_points
|
||||
ADD COLUMN IF NOT EXISTS user_id INT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS points INT NOT NULL DEFAULT 0;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS radio_listener_points_points_idx ON radio_listener_points (points);
|
||||
@@ -0,0 +1,13 @@
|
||||
-- CMS-owned editable content boxes (AtomCMS WriteableBox). Rendered on the
|
||||
-- public site and managed from housekeeping. Idempotent (MariaDB).
|
||||
CREATE TABLE IF NOT EXISTS website_writeable_boxes (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
icon VARCHAR(255) NULL,
|
||||
content TEXT NOT NULL,
|
||||
position INT NOT NULL DEFAULT 0,
|
||||
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP NULL,
|
||||
updated_at TIMESTAMP NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- Successful-login history (the AtomCMS "session logs" equivalent; our auth is
|
||||
-- stateless JWT so there's no sessions table). Idempotent (MariaDB).
|
||||
CREATE TABLE IF NOT EXISTS website_login_logs (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
ip VARCHAR(45) NOT NULL,
|
||||
user_agent VARCHAR(512) NULL,
|
||||
created_at TIMESTAMP NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY website_login_logs_user_id_idx (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 0008_add_bans_user_id_index.sql
|
||||
-- Adds an index on bans.user_id to speed up ban lookups by user (used by
|
||||
-- the access guard on every page request for logged-in users).
|
||||
CREATE INDEX IF NOT EXISTS `bans_user_id_index` ON `bans` (`user_id`);
|
||||
@@ -0,0 +1,29 @@
|
||||
-- 0009_radio_contests_giveaways_columns.sql
|
||||
-- Fills in the empty RadioContests and RadioGiveaways tables with meaningful
|
||||
-- columns. These tables were created in migration 0004 with only id/timestamps.
|
||||
|
||||
ALTER TABLE `radio_contests`
|
||||
ADD COLUMN IF NOT EXISTS `title` VARCHAR(255) NOT NULL AFTER `id`,
|
||||
ADD COLUMN IF NOT EXISTS `description` TEXT NULL AFTER `title`,
|
||||
ADD COLUMN IF NOT EXISTS `prize` VARCHAR(255) NULL AFTER `description`,
|
||||
ADD COLUMN IF NOT EXISTS `start_date` TIMESTAMP(0) NULL AFTER `prize`,
|
||||
ADD COLUMN IF NOT EXISTS `end_date` TIMESTAMP(0) NULL AFTER `start_date`,
|
||||
ADD COLUMN IF NOT EXISTS `winner_id` BIGINT UNSIGNED NULL AFTER `end_date`,
|
||||
ADD COLUMN IF NOT EXISTS `is_active` BOOLEAN NOT NULL DEFAULT true AFTER `winner_id`,
|
||||
ADD COLUMN IF NOT EXISTS `created_by` BIGINT UNSIGNED NULL AFTER `is_active`,
|
||||
MODIFY COLUMN `created_at` TIMESTAMP(0) NULL AFTER `created_by`,
|
||||
MODIFY COLUMN `updated_at` TIMESTAMP(0) NULL AFTER `created_at`;
|
||||
|
||||
ALTER TABLE `radio_giveaways`
|
||||
ADD COLUMN IF NOT EXISTS `title` VARCHAR(255) NOT NULL AFTER `id`,
|
||||
ADD COLUMN IF NOT EXISTS `description` TEXT NULL AFTER `title`,
|
||||
ADD COLUMN IF NOT EXISTS `prize` VARCHAR(255) NULL AFTER `description`,
|
||||
ADD COLUMN IF NOT EXISTS `prize_amount` INT NOT NULL DEFAULT 0 AFTER `prize`,
|
||||
ADD COLUMN IF NOT EXISTS `prize_currency` VARCHAR(50) NULL AFTER `prize_amount`,
|
||||
ADD COLUMN IF NOT EXISTS `start_date` TIMESTAMP(0) NULL AFTER `prize_currency`,
|
||||
ADD COLUMN IF NOT EXISTS `end_date` TIMESTAMP(0) NULL AFTER `start_date`,
|
||||
ADD COLUMN IF NOT EXISTS `winner_id` BIGINT UNSIGNED NULL AFTER `end_date`,
|
||||
ADD COLUMN IF NOT EXISTS `is_active` BOOLEAN NOT NULL DEFAULT true AFTER `winner_id`,
|
||||
ADD COLUMN IF NOT EXISTS `created_by` BIGINT UNSIGNED NULL AFTER `is_active`,
|
||||
MODIFY COLUMN `created_at` TIMESTAMP(0) NULL AFTER `created_by`,
|
||||
MODIFY COLUMN `updated_at` TIMESTAMP(0) NULL AFTER `created_at`;
|
||||
@@ -0,0 +1,114 @@
|
||||
-- CMS-owned tables required by the restored administration modules.
|
||||
-- Idempotent and deliberately free of foreign keys so it can be applied to
|
||||
-- existing emulator databases whose users table may use different engines.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS website_tickets (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
category VARCHAR(50) NOT NULL DEFAULT 'general',
|
||||
priority VARCHAR(20) NOT NULL DEFAULT 'normal',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
||||
creator_id INT NOT NULL,
|
||||
assignee_id INT NULL,
|
||||
closed_at DATETIME NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY website_tickets_creator_idx (creator_id),
|
||||
KEY website_tickets_assignee_idx (assignee_id),
|
||||
KEY website_tickets_status_idx (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS website_ticket_messages (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
ticket_id INT NOT NULL,
|
||||
user_id INT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
is_staff INT NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY website_ticket_messages_ticket_idx (ticket_id),
|
||||
KEY website_ticket_messages_user_idx (user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS website_ticket_templates (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
category VARCHAR(50) NOT NULL DEFAULT 'general',
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_watch (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
staff_id INT NOT NULL,
|
||||
target_user_id INT NOT NULL,
|
||||
reason VARCHAR(255) NOT NULL DEFAULT '',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY user_watch_unique (staff_id, target_user_id),
|
||||
KEY user_watch_target_idx (target_user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_audit_log (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
action VARCHAR(191) NOT NULL DEFAULT '',
|
||||
target VARCHAR(191) NOT NULL DEFAULT '',
|
||||
target_id INT NULL,
|
||||
details TEXT NULL,
|
||||
`before` TEXT NULL,
|
||||
`after` TEXT NULL,
|
||||
diff TEXT NULL,
|
||||
ip_address VARCHAR(45) NULL,
|
||||
created_at VARCHAR(64) NOT NULL DEFAULT '',
|
||||
updated_at VARCHAR(64) NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY admin_audit_log_user_idx (user_id),
|
||||
KEY admin_audit_log_target_idx (target, target_id),
|
||||
KEY admin_audit_log_action_idx (action)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_roles (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
slug VARCHAR(191) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL DEFAULT '',
|
||||
description TEXT NULL,
|
||||
created_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY acl_roles_slug_unique (slug)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_permissions (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
slug VARCHAR(191) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL DEFAULT '',
|
||||
created_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY acl_permissions_slug_unique (slug)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_model_roles (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
model_type VARCHAR(191) NOT NULL DEFAULT 'User',
|
||||
model_id INT NOT NULL,
|
||||
role_id INT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY acl_model_roles_role_idx (role_id),
|
||||
UNIQUE KEY acl_model_roles_unique (model_type, model_id, role_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS acl_model_permissions (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
model_type VARCHAR(191) NOT NULL DEFAULT 'Role',
|
||||
model_id INT NOT NULL,
|
||||
permission_id INT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY acl_model_permissions_model_idx (model_id),
|
||||
KEY acl_model_permissions_permission_idx (permission_id),
|
||||
UNIQUE KEY acl_model_permissions_unique (model_type, model_id, permission_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,37 @@
|
||||
-- Normalize ACL discriminator casing and ensure every emulator rank has a CMS role.
|
||||
UPDATE `acl_model_permissions` SET `model_type` = 'Role' WHERE LOWER(`model_type`) = 'role';
|
||||
UPDATE `acl_model_roles` SET `model_type` = 'User' WHERE LOWER(`model_type`) = 'user';
|
||||
|
||||
INSERT INTO `acl_roles` (`slug`, `title`, `description`)
|
||||
SELECT CONCAT('rank_', pr.id), COALESCE(NULLIF(pr.rank_name, ''), CONCAT('Rank ', pr.id)), 'CMS role synchronized from permission_ranks'
|
||||
FROM `permission_ranks` pr
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `acl_roles` ar WHERE ar.slug = CONCAT('rank_', pr.id));
|
||||
|
||||
-- Preserve the former moderator fallback as explicit ACL data.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
pr.id >= 3 AND (
|
||||
ap.slug = 'mod.dashboard' OR ap.slug = 'mod.actions' OR
|
||||
(ap.slug LIKE 'mod.%' AND ap.slug LIKE '%.view')
|
||||
)
|
||||
)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role' AND amp.model_id = ar.id AND amp.permission_id = ap.id
|
||||
);
|
||||
|
||||
-- Preserve the former administrator read-only fallback as explicit ACL data.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
pr.id >= 6 AND (ap.slug = 'admin.dashboard' OR (ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.view'))
|
||||
)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role' AND amp.model_id = ar.id AND amp.permission_id = ap.id
|
||||
);
|
||||
@@ -0,0 +1,104 @@
|
||||
-- Seed every permission used by the CMS. Migration 0010 created the ACL
|
||||
-- tables but did not populate acl_permissions, so migration 0011 could not
|
||||
-- assign any dashboard permission to rank roles.
|
||||
INSERT INTO `acl_permissions` (`slug`, `title`) VALUES
|
||||
('admin.dashboard', 'Admin dashboard'),
|
||||
('admin.users.view', 'View users'),
|
||||
('admin.users.edit', 'Edit users'),
|
||||
('admin.users.ban', 'Ban users'),
|
||||
('admin.users.reset_password', 'Reset user passwords'),
|
||||
('admin.room.view', 'View rooms'),
|
||||
('admin.room.edit', 'Edit rooms'),
|
||||
('admin.room.delete', 'Delete rooms'),
|
||||
('admin.bans.view', 'View bans'),
|
||||
('admin.news.view', 'View news'),
|
||||
('admin.news.edit', 'Edit news'),
|
||||
('admin.logs.view', 'View logs'),
|
||||
('admin.settings.view', 'View settings'),
|
||||
('admin.settings.edit', 'Edit settings'),
|
||||
('admin.permissions.manage', 'Manage permissions'),
|
||||
('admin.shop.view', 'View shop'),
|
||||
('admin.shop.edit', 'Edit shop'),
|
||||
('admin.wordfilter.view', 'View word filter'),
|
||||
('admin.wordfilter.edit', 'Edit word filter'),
|
||||
('admin.catalog.view', 'View catalog'),
|
||||
('admin.catalog.edit', 'Edit catalog'),
|
||||
('admin.rcon.execute', 'Execute RCON commands'),
|
||||
('admin.export', 'Export data'),
|
||||
('admin.radio.view', 'View radio admin'),
|
||||
('admin.radio.edit', 'Edit radio admin'),
|
||||
('admin.prefixes.view', 'View prefixes'),
|
||||
('admin.prefixes.edit', 'Edit prefixes'),
|
||||
('admin.tickets.view', 'View tickets'),
|
||||
('admin.tickets.edit', 'Edit tickets'),
|
||||
('admin.moderation.view', 'View moderation'),
|
||||
('admin.moderation.edit', 'Edit moderation'),
|
||||
('admin.events.view', 'View events'),
|
||||
('admin.events.edit', 'Edit events'),
|
||||
('admin.analytics.view', 'View analytics'),
|
||||
('admin.analytics.export', 'Export analytics'),
|
||||
('admin.devops.view', 'View DevOps'),
|
||||
('admin.devops.edit', 'Edit DevOps'),
|
||||
('admin.polls.view', 'View polls'),
|
||||
('admin.polls.edit', 'Edit polls'),
|
||||
('admin.notifications.view', 'View notifications'),
|
||||
('admin.notifications.edit', 'Edit notifications'),
|
||||
('admin.pages.view', 'View CMS pages'),
|
||||
('admin.pages.edit', 'Edit CMS pages'),
|
||||
('admin.banners.view', 'View banners'),
|
||||
('admin.banners.edit', 'Edit banners'),
|
||||
('admin.assets.import', 'Import assets'),
|
||||
('mod.dashboard', 'Moderator dashboard'),
|
||||
('mod.cfh.view', 'View calls for help'),
|
||||
('mod.cfh.edit', 'Manage calls for help'),
|
||||
('mod.actions', 'Execute moderator actions'),
|
||||
('mod.team.view', 'View moderator team'),
|
||||
('mod.tickets.view', 'View moderator tickets'),
|
||||
('mod.tickets.edit', 'Edit moderator tickets'),
|
||||
('mod.users.view', 'View users in moderation'),
|
||||
('mod.bans.view', 'View bans in moderation')
|
||||
ON DUPLICATE KEY UPDATE `title` = VALUES(`title`);
|
||||
|
||||
-- Keep rank roles synchronized for databases where 0011 already ran.
|
||||
INSERT INTO `acl_roles` (`slug`, `title`)
|
||||
SELECT CONCAT('rank_', pr.id), COALESCE(NULLIF(pr.rank_name, ''), CONCAT('Rank ', pr.id))
|
||||
FROM `permission_ranks` pr
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_roles` ar WHERE ar.slug = CONCAT('rank_', pr.id)
|
||||
);
|
||||
|
||||
-- Preserve moderator access as explicit ACL assignments.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
pr.id >= 3 AND (
|
||||
ap.slug = 'mod.dashboard' OR ap.slug = 'mod.actions' OR
|
||||
(ap.slug LIKE 'mod.%' AND ap.slug LIKE '%.view')
|
||||
)
|
||||
)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
|
||||
-- Preserve administrator read access as explicit ACL assignments.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
pr.id >= 6 AND (
|
||||
ap.slug = 'admin.dashboard' OR
|
||||
(ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.view')
|
||||
)
|
||||
)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
@@ -0,0 +1,136 @@
|
||||
CREATE TABLE IF NOT EXISTS `website_event_types` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`slug` VARCHAR(100) NOT NULL,
|
||||
`description` VARCHAR(500) NOT NULL DEFAULT '',
|
||||
`color` VARCHAR(20) NOT NULL DEFAULT '#3b82f6',
|
||||
`icon` VARCHAR(50) NOT NULL DEFAULT 'calendar',
|
||||
`is_active` INTEGER NOT NULL DEFAULT 1,
|
||||
`min_rank` INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE INDEX `website_event_types_slug_key`(`slug`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_events` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT NOT NULL,
|
||||
`type_id` INTEGER NOT NULL,
|
||||
`host_user_id` INTEGER NOT NULL,
|
||||
`room_id` INTEGER NULL,
|
||||
`starts_at` DATETIME(3) NOT NULL,
|
||||
`ends_at` DATETIME(3) NULL,
|
||||
`max_players` INTEGER NULL,
|
||||
`is_recurring` INTEGER NOT NULL DEFAULT 0,
|
||||
`recurrence_rule` VARCHAR(255) NULL,
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||
`image` VARCHAR(500) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
INDEX `website_events_type_id_idx`(`type_id`),
|
||||
INDEX `website_events_host_user_id_idx`(`host_user_id`),
|
||||
INDEX `website_events_starts_at_idx`(`starts_at`),
|
||||
INDEX `website_events_status_idx`(`status`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_events_type_id_fkey` FOREIGN KEY (`type_id`) REFERENCES `website_event_types`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_event_registrations` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`event_id` INTEGER NOT NULL,
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX `website_event_registrations_user_id_idx`(`user_id`),
|
||||
UNIQUE INDEX `website_event_registrations_event_id_user_id_key`(`event_id`, `user_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_event_registrations_event_id_fkey` FOREIGN KEY (`event_id`) REFERENCES `website_events`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_event_prizes` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`event_id` INTEGER NOT NULL,
|
||||
`position` INTEGER NOT NULL DEFAULT 1,
|
||||
`prize_type` VARCHAR(20) NOT NULL DEFAULT 'badge',
|
||||
`badge_code` VARCHAR(50) NULL,
|
||||
`credits` INTEGER NOT NULL DEFAULT 0,
|
||||
`pixels` INTEGER NOT NULL DEFAULT 0,
|
||||
`points` INTEGER NOT NULL DEFAULT 0,
|
||||
`item_id` INTEGER NULL,
|
||||
`description` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
INDEX `website_event_prizes_event_id_idx`(`event_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_event_prizes_event_id_fkey` FOREIGN KEY (`event_id`) REFERENCES `website_events`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_event_winners` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`event_id` INTEGER NOT NULL,
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`position` INTEGER NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX `website_event_winners_user_id_idx`(`user_id`),
|
||||
UNIQUE INDEX `website_event_winners_event_id_user_id_key`(`event_id`, `user_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_event_winners_event_id_fkey` FOREIGN KEY (`event_id`) REFERENCES `website_events`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_polls` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`description` TEXT NULL,
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||
`show_results` INTEGER NOT NULL DEFAULT 1,
|
||||
`multiple_choice` INTEGER NOT NULL DEFAULT 0,
|
||||
`starts_at` DATETIME(3) NULL,
|
||||
`ends_at` DATETIME(3) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
INDEX `website_polls_status_idx`(`status`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_poll_questions` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`poll_id` INTEGER NOT NULL,
|
||||
`question` VARCHAR(500) NOT NULL,
|
||||
`type` VARCHAR(20) NOT NULL DEFAULT 'single',
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`options` TEXT NOT NULL,
|
||||
INDEX `website_poll_questions_poll_id_idx`(`poll_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_poll_questions_poll_id_fkey` FOREIGN KEY (`poll_id`) REFERENCES `website_polls`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_poll_votes` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`question_id` INTEGER NOT NULL,
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`answer` VARCHAR(500) NOT NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
INDEX `website_poll_votes_user_id_idx`(`user_id`),
|
||||
UNIQUE INDEX `website_poll_votes_question_id_user_id_key`(`question_id`, `user_id`),
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `website_poll_votes_question_id_fkey` FOREIGN KEY (`question_id`) REFERENCES `website_poll_questions`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `website_banners` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`title` VARCHAR(255) NOT NULL DEFAULT '', `subtitle` VARCHAR(500) NOT NULL DEFAULT '',
|
||||
`image` VARCHAR(500) NOT NULL DEFAULT '', `link` VARCHAR(500) NOT NULL DEFAULT '',
|
||||
`color` VARCHAR(20) NOT NULL DEFAULT '', `is_active` INT NOT NULL DEFAULT 1,
|
||||
`sort_order` INT NOT NULL DEFAULT 0, `start_date` VARCHAR(50) NULL, `end_date` VARCHAR(50) NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `custom_prefixes` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT, `user_id` INT NOT NULL, `text` VARCHAR(50) NOT NULL,
|
||||
`color` VARCHAR(255) NOT NULL DEFAULT '#FFFFFF', `icon` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`effect` VARCHAR(50) NOT NULL DEFAULT '', `active` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`), INDEX `idx_custom_prefix_user` (`user_id`, `active`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS `custom_prefix_blacklist` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT, `word` VARCHAR(100) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uq_prefix_word` (`word`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
CREATE TABLE IF NOT EXISTS `custom_prefix_settings` (
|
||||
`key` VARCHAR(100) NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,35 @@
|
||||
UPDATE `acl_model_permissions` SET `model_type` = 'Role' WHERE LOWER(`model_type`) = 'role';
|
||||
UPDATE `acl_model_roles` SET `model_type` = 'User' WHERE LOWER(`model_type`) = 'user';
|
||||
|
||||
INSERT INTO `acl_permissions` (`slug`, `title`) VALUES
|
||||
('admin.assets.import', 'Import assets'),
|
||||
('admin.permissions.manage', 'Manage permissions')
|
||||
ON DUPLICATE KEY UPDATE `title` = VALUES(`title`);
|
||||
|
||||
INSERT INTO `acl_roles` (`slug`, `title`, `description`)
|
||||
SELECT CONCAT('rank_', pr.id), COALESCE(NULLIF(pr.rank_name, ''), CONCAT('Rank ', pr.id)), 'CMS role synchronized from permission_ranks'
|
||||
FROM `permission_ranks` pr
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `acl_roles` ar WHERE ar.slug = CONCAT('rank_', pr.id));
|
||||
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
CROSS JOIN `acl_permissions` ap
|
||||
WHERE pr.id = (SELECT MAX(id) FROM `permission_ranks`)
|
||||
AND ap.slug IN ('admin.permissions.manage', 'admin.assets.import')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role' AND amp.model_id = ar.id AND amp.permission_id = ap.id
|
||||
);
|
||||
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `website_housekeeping_permissions` whp
|
||||
JOIN `acl_permissions` ap ON ap.slug = whp.permission
|
||||
JOIN `permission_ranks` pr ON pr.id >= whp.min_rank
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role' AND amp.model_id = ar.id AND amp.permission_id = ap.id
|
||||
);
|
||||
@@ -0,0 +1,33 @@
|
||||
INSERT INTO `acl_permissions` (`slug`, `title`) VALUES
|
||||
('admin.radio.view', 'View radio admin'),
|
||||
('admin.radio.edit', 'Edit radio admin')
|
||||
ON DUPLICATE KEY UPDATE `title` = VALUES(`title`);
|
||||
|
||||
-- Rank >= 6 gets radio view (matches other admin.*.view grants from 0012).
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON ap.slug = 'admin.radio.view'
|
||||
WHERE pr.id >= 6
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
|
||||
-- Highest rank gets radio edit (same pattern as assets.import / permissions.manage).
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
CROSS JOIN `acl_permissions` ap
|
||||
WHERE pr.id = (SELECT MAX(id) FROM `permission_ranks`)
|
||||
AND ap.slug = 'admin.radio.edit'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE `website_housekeeping_permissions`
|
||||
ADD COLUMN `group_name` VARCHAR(100) DEFAULT NULL AFTER `description`,
|
||||
ADD COLUMN `depends_on` VARCHAR(255) DEFAULT NULL AFTER `group_name`,
|
||||
ADD COLUMN `updated_by` INT DEFAULT NULL AFTER `updated_at`;
|
||||
|
||||
-- Migration tracking (no model change needed for audit log usage)
|
||||
@@ -0,0 +1,4 @@
|
||||
-- CMS JWT invalidation counter on users (stateless NextAuth sessions).
|
||||
-- Idempotent (MariaDB).
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS website_jwt_version INT NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1,69 @@
|
||||
-- Repair admin ACL so ranks that can open the panel also see/use admin sections.
|
||||
-- Root cause: sidebar filters by ACL; seed only granted some admin.*.view rows,
|
||||
-- and later sync from housekeeping could leave mid/high ranks incomplete.
|
||||
|
||||
-- 1) Every rank that already has admin.dashboard gets ALL admin.* permissions.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `acl_roles` ar
|
||||
JOIN `acl_permissions` ap ON ap.slug LIKE 'admin.%'
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM `acl_model_permissions` amp
|
||||
JOIN `acl_permissions` apdash ON apdash.id = amp.permission_id
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND apdash.slug = 'admin.dashboard'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `acl_model_permissions` amp2
|
||||
WHERE amp2.model_type = 'Role'
|
||||
AND amp2.model_id = ar.id
|
||||
AND amp2.permission_id = ap.id
|
||||
);
|
||||
|
||||
-- 2) Safety net matching original seed: ranks >= 6 get all admin.*.view + dashboard.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
ap.slug = 'admin.dashboard'
|
||||
OR (ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.view')
|
||||
)
|
||||
WHERE pr.id >= 6
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
|
||||
-- 3) Ranks >= 7 also get edit/manage/execute tools used by the sidebar.
|
||||
INSERT INTO `acl_model_permissions` (`model_type`, `model_id`, `permission_id`)
|
||||
SELECT 'Role', ar.id, ap.id
|
||||
FROM `permission_ranks` pr
|
||||
JOIN `acl_roles` ar ON ar.slug = CONCAT('rank_', pr.id)
|
||||
JOIN `acl_permissions` ap ON (
|
||||
(ap.slug LIKE 'admin.%' AND ap.slug LIKE '%.edit')
|
||||
OR ap.slug IN (
|
||||
'admin.permissions.manage',
|
||||
'admin.rcon.execute',
|
||||
'admin.assets.import',
|
||||
'admin.export',
|
||||
'admin.analytics.export',
|
||||
'admin.users.ban',
|
||||
'admin.users.reset_password',
|
||||
'admin.room.delete'
|
||||
)
|
||||
)
|
||||
WHERE pr.id >= 7
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM `acl_model_permissions` amp
|
||||
WHERE amp.model_type = 'Role'
|
||||
AND amp.model_id = ar.id
|
||||
AND amp.permission_id = ap.id
|
||||
);
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS `website_login_logs` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INT NOT NULL,
|
||||
`ip` VARCHAR(45) NOT NULL,
|
||||
`user_agent` VARCHAR(512) DEFAULT NULL,
|
||||
`created_at` TIMESTAMP NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `website_login_logs_user_id_index` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
Reference in new issue
Block a user