diff --git a/drizzle/drafts/0000_premium_spirit.sql b/drizzle/drafts/0000_premium_spirit.sql new file mode 100644 index 00000000..717f9d20 --- /dev/null +++ b/drizzle/drafts/0000_premium_spirit.sql @@ -0,0 +1,2199 @@ +CREATE TABLE `achievements` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(64) NOT NULL DEFAULT 'ACH_', + `category` varchar(12) NOT NULL DEFAULT 'identity', + `level` int NOT NULL DEFAULT 1, + `reward_amount` int NOT NULL DEFAULT 100, + `reward_type` int NOT NULL DEFAULT 0, + `points` int DEFAULT 10, + `progress_needed` int NOT NULL DEFAULT 1, + CONSTRAINT `achievements_name_level_pk` PRIMARY KEY(`name`,`level`), + CONSTRAINT `achievements_id_unique` UNIQUE(`id`) +); +--> statement-breakpoint +CREATE TABLE `achievements_talents` ( + `id` int AUTO_INCREMENT NOT NULL, + `type` varchar(11) NOT NULL DEFAULT 'citizenship', + `level` int NOT NULL DEFAULT 0, + `achievement_ids` varchar(100) NOT NULL DEFAULT '', + `achievement_levels` varchar(100) NOT NULL DEFAULT '', + `reward_furni` varchar(100) NOT NULL DEFAULT '', + `reward_perks` varchar(100) NOT NULL DEFAULT '', + `reward_badges` varchar(100) NOT NULL DEFAULT '', + CONSTRAINT `achievements_talents_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `acl_model_permissions` ( + `id` int AUTO_INCREMENT NOT NULL, + `model_type` varchar(191) NOT NULL DEFAULT 'Role', + `model_id` int NOT NULL, + `permission_id` int NOT NULL, + CONSTRAINT `acl_model_permissions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `acl_model_roles` ( + `id` int AUTO_INCREMENT NOT NULL, + `model_type` varchar(191) NOT NULL DEFAULT 'User', + `model_id` int NOT NULL, + `role_id` int NOT NULL, + CONSTRAINT `acl_model_roles_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `acl_permissions` ( + `id` int AUTO_INCREMENT NOT NULL, + `slug` varchar(191) NOT NULL, + `title` varchar(255) NOT NULL DEFAULT '', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime, + CONSTRAINT `acl_permissions_id` PRIMARY KEY(`id`), + CONSTRAINT `acl_permissions_slug_unique` UNIQUE(`slug`) +); +--> statement-breakpoint +CREATE TABLE `acl_roles` ( + `id` int AUTO_INCREMENT NOT NULL, + `slug` varchar(191) NOT NULL, + `title` varchar(255) NOT NULL DEFAULT '', + `description` text, + `created_at` datetime DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime, + CONSTRAINT `acl_roles_id` PRIMARY KEY(`id`), + CONSTRAINT `acl_roles_slug_unique` UNIQUE(`slug`) +); +--> statement-breakpoint +CREATE TABLE `admin_audit_log` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `action` varchar(191) NOT NULL DEFAULT '', + `target` varchar(191) NOT NULL DEFAULT '', + `target_id` int, + `details` text, + `before` text, + `after` text, + `diff` text, + `ip_address` varchar(45), + `created_at` varchar(64) NOT NULL DEFAULT '', + `updated_at` varchar(64), + CONSTRAINT `admin_audit_log_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `alert_logs` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `type` varchar(255) NOT NULL, + `severity` varchar(255) NOT NULL, + `message` text NOT NULL, + `context` longtext, + `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, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `alert_logs_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `bans` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `ip` varchar(50) NOT NULL DEFAULT '', + `machine_id` varchar(255) NOT NULL DEFAULT '', + `user_staff_id` int NOT NULL, + `timestamp` int NOT NULL, + `ban_expire` int NOT NULL DEFAULT 0, + `ban_reason` varchar(200) NOT NULL DEFAULT '', + `type` enum('account','ip','machine','super') NOT NULL DEFAULT 'account', + `cfh_topic` int NOT NULL DEFAULT -1, + CONSTRAINT `bans_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `bots` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `room_id` int NOT NULL DEFAULT 0, + `name` varchar(25) NOT NULL DEFAULT '', + `motto` varchar(100) NOT NULL DEFAULT '', + `figure` varchar(500) NOT NULL DEFAULT '', + `gender` varchar(1) NOT NULL DEFAULT 'M', + `x` int NOT NULL DEFAULT 0, + `y` int NOT NULL DEFAULT 0, + `z` double NOT NULL DEFAULT 0, + `rot` int NOT NULL DEFAULT 0, + `chat_lines` varchar(5112) NOT NULL DEFAULT '', + `chat_auto` varchar(1) NOT NULL DEFAULT '1', + `chat_random` varchar(1) NOT NULL DEFAULT '1', + `chat_delay` int NOT NULL DEFAULT 5, + `dance` int NOT NULL DEFAULT 0, + `freeroam` varchar(1) NOT NULL DEFAULT '0', + `type` varchar(32) NOT NULL DEFAULT 'generic', + `effect` int NOT NULL DEFAULT 0, + `bubble_id` int DEFAULT 31, + CONSTRAINT `bots_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `calendar_campaigns` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `image` varchar(255) NOT NULL DEFAULT '', + `start_timestamp` int NOT NULL DEFAULT 0, + `total_days` int NOT NULL DEFAULT 30, + `lock_expired` varchar(1) NOT NULL DEFAULT '1', + `enabled` varchar(1) NOT NULL DEFAULT '1', + CONSTRAINT `calendar_campaigns_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `calendar_rewards` ( + `id` int AUTO_INCREMENT NOT NULL, + `campaign_id` int NOT NULL DEFAULT 0, + `product_name` varchar(128) NOT NULL DEFAULT '', + `custom_image` varchar(128) NOT NULL DEFAULT '', + `credits` int NOT NULL DEFAULT 0, + `pixels` int NOT NULL DEFAULT 0, + `points` int NOT NULL DEFAULT 0, + `points_type` int NOT NULL DEFAULT 0, + `badge` varchar(25) NOT NULL DEFAULT '', + `item_id` int NOT NULL DEFAULT 0, + `subscription_type` varchar(128) DEFAULT '', + `subscription_days` int NOT NULL DEFAULT 0, + CONSTRAINT `calendar_rewards_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `camera_web` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `room_id` int NOT NULL DEFAULT 0, + `timestamp` int NOT NULL, + `url` varchar(128) NOT NULL DEFAULT '', + CONSTRAINT `camera_web_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_clothing` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(75) NOT NULL, + `setid` varchar(200) NOT NULL, + CONSTRAINT `catalog_clothing_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_club_offers` ( + `id` int NOT NULL, + `enabled` varchar(1) NOT NULL DEFAULT '1', + `name` varchar(35) NOT NULL, + `days` int NOT NULL, + `credits` int NOT NULL DEFAULT 10, + `points` int NOT NULL DEFAULT 0, + `points_type` int NOT NULL DEFAULT 0, + `type` varchar(19) NOT NULL DEFAULT 'HC', + `deal` varchar(1) NOT NULL DEFAULT '0', + `giftable` varchar(1) NOT NULL DEFAULT '0', + CONSTRAINT `catalog_club_offers_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_featured_pages` ( + `slot_id` int NOT NULL, + `image` varchar(70) NOT NULL DEFAULT '', + `caption` varchar(130) NOT NULL DEFAULT '', + `type` varchar(12) NOT NULL DEFAULT 'page_name', + `expire_timestamp` int NOT NULL DEFAULT -1, + `page_name` varchar(30) NOT NULL DEFAULT '', + `page_id` int NOT NULL DEFAULT 0, + `product_name` varchar(40) NOT NULL DEFAULT '', + CONSTRAINT `catalog_featured_pages_slot_id` PRIMARY KEY(`slot_id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_items` ( + `id` int AUTO_INCREMENT NOT NULL, + `item_ids` varchar(666) NOT NULL, + `page_id` int NOT NULL, + `catalog_name` varchar(100) NOT NULL, + `cost_credits` int NOT NULL DEFAULT 3, + `cost_points` int NOT NULL DEFAULT 0, + `points_type` int NOT NULL DEFAULT 0, + `amount` int NOT NULL DEFAULT 1, + `limited_stack` int NOT NULL DEFAULT 0, + `limited_sells` int NOT NULL DEFAULT 0, + `order_number` int NOT NULL DEFAULT 1, + `offer_id` int NOT NULL DEFAULT -1, + `song_id` int unsigned NOT NULL DEFAULT 0, + `extradata` varchar(500) NOT NULL DEFAULT '', + `haveOffer` varchar(255) NOT NULL DEFAULT '1', + `club_only` varchar(1) NOT NULL DEFAULT '0', + CONSTRAINT `catalog_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_items_bc` ( + `id` int AUTO_INCREMENT NOT NULL, + `item_ids` varchar(666) NOT NULL, + `page_id` int NOT NULL, + `catalog_name` varchar(100) NOT NULL, + `order_number` int NOT NULL DEFAULT 1, + `extradata` varchar(500) NOT NULL DEFAULT '', + CONSTRAINT `catalog_items_bc_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_items_limited` ( + `catalog_item_id` int NOT NULL, + `number` int NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `timestamp` int NOT NULL DEFAULT 0, + `item_id` int NOT NULL DEFAULT 0, + CONSTRAINT `catalog_items_limited_catalogItemId_number` UNIQUE(`catalog_item_id`,`number`) +); +--> statement-breakpoint +CREATE TABLE `catalog_pages` ( + `id` int AUTO_INCREMENT NOT NULL, + `parent_id` int NOT NULL DEFAULT -1, + `caption_save` varchar(25) NOT NULL DEFAULT '', + `caption` varchar(128) NOT NULL, + `page_layout` varchar(26) NOT NULL DEFAULT 'default_3x3', + `icon_color` int NOT NULL DEFAULT 1, + `icon_image` int NOT NULL DEFAULT 1, + `min_rank` int NOT NULL DEFAULT 1, + `order_num` int NOT NULL DEFAULT 1, + `visible` varchar(1) NOT NULL DEFAULT '1', + `enabled` varchar(1) NOT NULL DEFAULT '1', + `club_only` varchar(1) NOT NULL DEFAULT '0', + `vip_only` varchar(1) NOT NULL DEFAULT '0', + `page_headline` varchar(1024) NOT NULL DEFAULT '', + `page_teaser` varchar(64) NOT NULL DEFAULT '', + `page_special` varchar(2048), + `page_text1` text, + `page_text2` text, + `page_text_details` text, + `page_text_teaser` text, + `room_id` int DEFAULT 0, + `includes` varchar(128) NOT NULL DEFAULT '', + CONSTRAINT `catalog_pages_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_pages_bc` ( + `id` int AUTO_INCREMENT NOT NULL, + `parent_id` int NOT NULL DEFAULT -1, + `caption` varchar(128) NOT NULL, + `page_layout` varchar(26) NOT NULL DEFAULT 'default_3x3', + `icon_color` int NOT NULL DEFAULT 1, + `icon_image` int NOT NULL DEFAULT 1, + `order_num` int NOT NULL DEFAULT 1, + `visible` varchar(1) NOT NULL DEFAULT '1', + `enabled` varchar(1) NOT NULL DEFAULT '1', + `page_headline` varchar(1024) NOT NULL DEFAULT '', + `page_teaser` varchar(64) NOT NULL DEFAULT '', + `page_special` varchar(2048), + `page_text1` text, + `page_text2` text, + `page_text_details` text, + `page_text_teaser` text, + CONSTRAINT `catalog_pages_bc_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `catalog_target_offers` ( + `id` int AUTO_INCREMENT NOT NULL, + `offer_code` varchar(32) NOT NULL, + `title` varchar(128) NOT NULL DEFAULT '', + `description` varchar(2048) NOT NULL DEFAULT '', + `image` varchar(128) NOT NULL, + `icon` varchar(128) NOT NULL, + `end_timestamp` int NOT NULL, + `credits` int NOT NULL DEFAULT 10, + `points` int NOT NULL DEFAULT 10, + `points_type` int NOT NULL DEFAULT 5, + `purchase_limit` int NOT NULL DEFAULT 5, + `catalog_item` int NOT NULL, + `vars` varchar(1024) NOT NULL DEFAULT '', + CONSTRAINT `catalog_target_offers_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `chatlogs_private` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_from_id` int NOT NULL, + `user_to_id` int NOT NULL, + `message` varchar(255) NOT NULL, + `timestamp` int NOT NULL DEFAULT 0, + CONSTRAINT `chatlogs_private_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `claimed_referral_logs` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `ip_address` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `claimed_referral_logs_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `crafting_altars_recipes` ( + `altar_id` int NOT NULL, + `recipe_id` int NOT NULL, + CONSTRAINT `crafting_altars_recipes_altarId_recipeId` UNIQUE(`altar_id`,`recipe_id`) +); +--> statement-breakpoint +CREATE TABLE `crafting_recipes` ( + `id` int NOT NULL, + `product_name` varchar(64) NOT NULL, + `reward` int NOT NULL, + `enabled` varchar(1) NOT NULL DEFAULT '1', + `achievement` varchar(255) NOT NULL DEFAULT '', + `secret` varchar(1) NOT NULL DEFAULT '0', + `limited` varchar(1) NOT NULL DEFAULT '0', + `remaining` int NOT NULL DEFAULT 0, + CONSTRAINT `crafting_recipes_id` PRIMARY KEY(`id`), + CONSTRAINT `crafting_recipes_product_name_unique` UNIQUE(`product_name`), + CONSTRAINT `crafting_recipes_productName` UNIQUE(`product_name`) +); +--> statement-breakpoint +CREATE TABLE `daily_challenges` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `description` varchar(255) NOT NULL, + `game` varchar(255), + `type` varchar(255) NOT NULL, + `target` int NOT NULL, + `reward_currency` varchar(255) NOT NULL DEFAULT 'credits', + `reward_amount` int NOT NULL, + `date` date NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `daily_challenges_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `email_templates` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `subject` varchar(255) NOT NULL, + `body` text NOT NULL, + `variables` text, + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `email_templates_id` PRIMARY KEY(`id`), + CONSTRAINT `email_templates_name_unique` UNIQUE(`name`) +); +--> statement-breakpoint +CREATE TABLE `emulator_errors` ( + `id` int AUTO_INCREMENT NOT NULL, + `timestamp` int NOT NULL DEFAULT 0, + `version` varchar(64) NOT NULL, + `build_hash` varchar(64) NOT NULL, + `type` varchar(32) NOT NULL DEFAULT 'Exception', + `stacktrace` binary NOT NULL, + CONSTRAINT `emulator_errors_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `emulator_settings` ( + `key` varchar(100) NOT NULL, + `value` varchar(512) NOT NULL, + CONSTRAINT `emulator_settings_key` PRIMARY KEY(`key`) +); +--> statement-breakpoint +CREATE TABLE `emulator_texts` ( + `key` varchar(100) NOT NULL, + `value` varchar(4096) NOT NULL, + CONSTRAINT `emulator_texts_key` PRIMARY KEY(`key`) +); +--> statement-breakpoint +CREATE TABLE `game_chat_messages` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `room_id` bigint unsigned NOT NULL, + `user_id` bigint unsigned NOT NULL, + `message` text NOT NULL, + `type` varchar(255) NOT NULL DEFAULT 'chat', + `reactions` longtext, + `created_at` timestamp NOT NULL, + CONSTRAINT `game_chat_messages_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `game_invitations` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `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` int NOT NULL DEFAULT 0, + `bet_currency` varchar(255) NOT NULL DEFAULT 'credits', + `room_id` bigint unsigned, + `expires_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_invitations_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `game_level_rewards` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `game` varchar(255) NOT NULL, + `level` int NOT NULL, + `reward_type` varchar(255) NOT NULL, + `reward_currency` varchar(255), + `reward_amount` int NOT NULL DEFAULT 0, + `claimed` boolean NOT NULL DEFAULT false, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_level_rewards_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `game_ranks` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `slug` varchar(255) NOT NULL, + `icon` varchar(255) NOT NULL DEFAULT '⭐', + `color` varchar(255) NOT NULL DEFAULT '#ffffff', + `min_points` int NOT NULL DEFAULT 0, + `max_points` int, + `tier` int NOT NULL DEFAULT 1, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_ranks_id` PRIMARY KEY(`id`), + CONSTRAINT `game_ranks_slug_unique` UNIQUE(`slug`) +); +--> statement-breakpoint +CREATE TABLE `game_replays` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `game` varchar(255) NOT NULL, + `player1_id` bigint unsigned NOT NULL, + `player2_id` bigint unsigned, + `game_data` longtext NOT NULL, + `moves` longtext NOT NULL, + `winner` varchar(255), + `player1_score` int NOT NULL DEFAULT 0, + `player2_score` int NOT NULL DEFAULT 0, + `started_at` timestamp NOT NULL, + `ended_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_replays_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `game_reward_claims` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `game` varchar(50) NOT NULL, + `wins_required` int NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_reward_claims_id` PRIMARY KEY(`id`), + CONSTRAINT `game_reward_claims_userId_game_winsRequired` UNIQUE(`user_id`,`game`,`wins_required`) +); +--> statement-breakpoint +CREATE TABLE `game_rooms` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `game` varchar(50) NOT NULL, + `player1_id` int unsigned NOT NULL, + `player2_id` int unsigned, + `state` longtext, + `turn` int unsigned NOT NULL DEFAULT 1, + `winner_id` int unsigned, + `status` varchar(8) NOT NULL DEFAULT 'waiting', + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_rooms_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `game_scores` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int unsigned NOT NULL, + `game` varchar(50) NOT NULL, + `score` int NOT NULL DEFAULT 0, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `game_scores_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `gift_wrappers` ( + `id` int AUTO_INCREMENT NOT NULL, + `sprite_id` int NOT NULL, + `item_id` int NOT NULL, + `type` varchar(7) NOT NULL DEFAULT 'wrapper', + CONSTRAINT `gift_wrappers_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `groups_items` ( + `type` varchar(6) NOT NULL, + `id` int NOT NULL, + `firstvalue` varchar(255) NOT NULL, + `secondvalue` varchar(2000) NOT NULL, + `enabled` varchar(1) NOT NULL DEFAULT '1', + CONSTRAINT `groups_items_id_type_pk` PRIMARY KEY(`id`,`type`) +); +--> statement-breakpoint +CREATE TABLE `guild_forum_views` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `guild_id` int NOT NULL, + `timestamp` int NOT NULL, + CONSTRAINT `guild_forum_views_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `guilds` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `name` varchar(50) NOT NULL DEFAULT '', + `description` varchar(250) NOT NULL DEFAULT '', + `room_id` int NOT NULL DEFAULT 0, + `state` int NOT NULL DEFAULT 0, + `rights` varchar(1) NOT NULL DEFAULT '0', + `color_one` int NOT NULL DEFAULT 0, + `color_two` int NOT NULL DEFAULT 0, + `badge` varchar(256) NOT NULL DEFAULT '', + `date_created` int NOT NULL, + `forum` varchar(1) NOT NULL DEFAULT '0', + `read_forum` varchar(8) NOT NULL DEFAULT 'EVERYONE', + `post_messages` varchar(8) NOT NULL DEFAULT 'EVERYONE', + `post_threads` varchar(8) NOT NULL DEFAULT 'EVERYONE', + `mod_forum` varchar(6) NOT NULL DEFAULT 'ADMINS', + CONSTRAINT `guilds_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `guilds_elements` ( + `id` int NOT NULL, + `firstvalue` varchar(300) NOT NULL, + `secondvalue` varchar(300) NOT NULL, + `type` varchar(50) NOT NULL, + `enabled` varchar(1) NOT NULL DEFAULT '1', + CONSTRAINT `guilds_elements_id_type` UNIQUE(`id`,`type`) +); +--> statement-breakpoint +CREATE TABLE `guilds_forums_comments` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `thread_id` int unsigned NOT NULL DEFAULT 0, + `user_id` int NOT NULL DEFAULT 0, + `message` text NOT NULL, + `created_at` int NOT NULL DEFAULT 0, + `state` int NOT NULL DEFAULT 0, + `admin_id` int NOT NULL DEFAULT 0, + CONSTRAINT `guilds_forums_comments_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `guilds_forums_threads` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `guild_id` int DEFAULT 0, + `opener_id` int DEFAULT 0, + `subject` varchar(255) DEFAULT '', + `posts_count` int DEFAULT 0, + `created_at` int DEFAULT 0, + `updated_at` int DEFAULT 0, + `state` int DEFAULT 0, + `pinned` tinyint DEFAULT 0, + `locked` tinyint DEFAULT 0, + `admin_id` int DEFAULT 0, + CONSTRAINT `guilds_forums_threads_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `guilds_members` ( + `id` int AUTO_INCREMENT NOT NULL, + `guild_id` int NOT NULL DEFAULT 0, + `user_id` int NOT NULL DEFAULT 0, + `level_id` int NOT NULL DEFAULT 0, + `member_since` int NOT NULL DEFAULT 0, + CONSTRAINT `guilds_members_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `hotelview_news` ( + `id` int AUTO_INCREMENT NOT NULL, + `title` varchar(100) NOT NULL, + `text` varchar(500) NOT NULL, + `button_text` varchar(50) NOT NULL, + `button_type` varchar(6) NOT NULL DEFAULT 'web', + `button_link` varchar(200) NOT NULL, + `image` varchar(200) NOT NULL, + CONSTRAINT `hotelview_news_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `items` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `room_id` int NOT NULL DEFAULT 0, + `item_id` int unsigned DEFAULT 0, + `wall_pos` varchar(20) NOT NULL DEFAULT '', + `x` int NOT NULL DEFAULT 0, + `y` int NOT NULL DEFAULT 0, + `z` double NOT NULL DEFAULT 0, + `rot` int NOT NULL DEFAULT 0, + `extra_data` varchar(1024) NOT NULL DEFAULT '', + `wired_data` varchar(10000) NOT NULL DEFAULT '', + `limited_data` varchar(10) NOT NULL DEFAULT '0:0', + `guild_id` int NOT NULL DEFAULT 0, + CONSTRAINT `items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `items_base` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `sprite_id` int NOT NULL DEFAULT 0, + `public_name` varchar(56) NOT NULL DEFAULT '', + `item_name` varchar(70) NOT NULL, + `type` varchar(3) NOT NULL DEFAULT 's', + `width` int NOT NULL DEFAULT 1, + `length` int NOT NULL DEFAULT 1, + `stack_height` double NOT NULL DEFAULT 0, + `allow_stack` tinyint NOT NULL DEFAULT 1, + `allow_sit` tinyint NOT NULL DEFAULT 0, + `allow_lay` tinyint NOT NULL DEFAULT 0, + `allow_walk` tinyint NOT NULL DEFAULT 0, + `allow_gift` tinyint NOT NULL DEFAULT 1, + `allow_trade` tinyint NOT NULL DEFAULT 1, + `allow_recycle` tinyint NOT NULL DEFAULT 0, + `allow_marketplace_sell` tinyint NOT NULL DEFAULT 0, + `allow_inventory_stack` tinyint NOT NULL DEFAULT 1, + `interaction_type` varchar(500) NOT NULL DEFAULT 'default', + `interaction_modes_count` int NOT NULL DEFAULT 1, + `vending_ids` varchar(255) NOT NULL DEFAULT '0', + `multiheight` varchar(50) NOT NULL DEFAULT '0', + `customparams` varchar(25600) NOT NULL DEFAULT '', + `effect_id_male` int NOT NULL DEFAULT 0, + `effect_id_female` int NOT NULL DEFAULT 0, + `clothing_on_walk` varchar(255) NOT NULL DEFAULT '', + CONSTRAINT `items_base_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `items_crackable` ( + `item_id` int NOT NULL, + `item_name` varchar(255) NOT NULL, + `count` int NOT NULL, + `prizes` varchar(860) NOT NULL DEFAULT '179:1', + `achievement_tick` varchar(64) NOT NULL, + `achievement_cracked` varchar(64) NOT NULL, + `required_effect` int NOT NULL DEFAULT 0, + `subscription_duration` int, + `subscription_type` varchar(255), + CONSTRAINT `items_crackable_item_id` PRIMARY KEY(`item_id`) +); +--> statement-breakpoint +CREATE TABLE `items_highscore_data` ( + `id` int AUTO_INCREMENT NOT NULL, + `item_id` int NOT NULL, + `user_ids` varchar(500) NOT NULL, + `score` int NOT NULL, + `is_win` tinyint DEFAULT 0, + `timestamp` int NOT NULL, + CONSTRAINT `items_highscore_data_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `logs_furni_imports` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `timestamp` int unsigned NOT NULL, + `user_id` int NOT NULL, + `batch` tinyint NOT NULL DEFAULT 0, + `total_items` int NOT NULL DEFAULT 0, + `succeeded` int NOT NULL DEFAULT 0, + `failed` int NOT NULL DEFAULT 0, + `details` text NOT NULL, + `rolled_back` tinyint NOT NULL DEFAULT 0, + `rolled_back_at` int unsigned, + `rolled_back_by` int, + CONSTRAINT `logs_furni_imports_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `logs_hc_payday` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `timestamp` int unsigned, + `user_id` int, + `hc_streak` int unsigned, + `total_coins_spent` int unsigned, + `reward_coins_spent` int unsigned, + `reward_streak` int unsigned, + `total_payout` int unsigned, + `currency` varchar(255), + `claimed` tinyint DEFAULT 0, + CONSTRAINT `logs_hc_payday_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `logs_shop_purchases` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `timestamp` int unsigned, + `user_id` int, + `catalog_item_id` int unsigned, + `item_ids` text, + `catalog_name` varchar(255), + `cost_credits` int, + `cost_points` int, + `points_type` int, + `amount` int, + CONSTRAINT `logs_shop_purchases_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `marketplace_items` ( + `id` int AUTO_INCREMENT NOT NULL, + `item_id` int NOT NULL, + `user_id` int NOT NULL, + `price` int NOT NULL, + `timestamp` int NOT NULL, + `sold_timestamp` int NOT NULL DEFAULT 0, + `state` int NOT NULL DEFAULT 1, + CONSTRAINT `marketplace_items_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `messenger_categories` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(25) NOT NULL, + `user_id` int NOT NULL, + CONSTRAINT `messenger_categories_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `messenger_friendrequests` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_to_id` int NOT NULL DEFAULT 0, + `user_from_id` int NOT NULL DEFAULT 0, + CONSTRAINT `messenger_friendrequests_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `messenger_friendships` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_one_id` int NOT NULL DEFAULT 0, + `user_two_id` int NOT NULL DEFAULT 0, + `relation` int NOT NULL DEFAULT 0, + `friends_since` int NOT NULL DEFAULT 0, + `category` int NOT NULL DEFAULT 0, + CONSTRAINT `messenger_friendships_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `messenger_offline` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `user_from_id` int NOT NULL DEFAULT 0, + `message` varchar(500) NOT NULL, + `sended_on` int NOT NULL, + CONSTRAINT `messenger_offline_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `navigator_filter` ( + `key` varchar(11) NOT NULL, + `field` varchar(32) NOT NULL, + `compare` varchar(18) NOT NULL, + `database_query` varchar(1024) NOT NULL, + CONSTRAINT `navigator_filter_key` PRIMARY KEY(`key`) +); +--> statement-breakpoint +CREATE TABLE `navigator_flatcats` ( + `id` int AUTO_INCREMENT NOT NULL, + `min_rank` int NOT NULL DEFAULT 0, + `caption_save` varchar(32) NOT NULL DEFAULT 'caption_save', + `caption` varchar(100) NOT NULL, + `canTrade` varchar(255) NOT NULL DEFAULT '1', + `max_user_count` int NOT NULL DEFAULT 100, + `public` varchar(1) NOT NULL DEFAULT '0', + `list_type` int NOT NULL DEFAULT 0, + `order_num` int NOT NULL DEFAULT 0, + CONSTRAINT `navigator_flatcats_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `navigator_publiccats` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(32) NOT NULL DEFAULT 'Staff Picks', + `image` varchar(1) NOT NULL DEFAULT '0', + `visible` varchar(1) NOT NULL DEFAULT '1', + `order_num` int NOT NULL DEFAULT 0, + CONSTRAINT `navigator_publiccats_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `nux_gifts` ( + `id` int AUTO_INCREMENT NOT NULL, + `type` varchar(4) NOT NULL DEFAULT 'item', + `value` varchar(32) NOT NULL, + `image` varchar(256) NOT NULL, + CONSTRAINT `nux_gifts_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `old_guilds_forums` ( + `id` int AUTO_INCREMENT NOT NULL, + `guild_id` int NOT NULL, + `user_id` int NOT NULL, + `subject` mediumtext NOT NULL, + `message` longtext NOT NULL, + `state` varchar(15) NOT NULL DEFAULT 'OPEN', + `admin_id` int NOT NULL DEFAULT 0, + `pinned` varchar(1) NOT NULL DEFAULT '0', + `locked` varchar(1) NOT NULL DEFAULT '0', + `timestamp` int NOT NULL, + CONSTRAINT `old_guilds_forums_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `old_guilds_forums_comments` ( + `id` int AUTO_INCREMENT NOT NULL, + `thread_id` int NOT NULL, + `user_id` int NOT NULL, + `timestamp` int NOT NULL, + `message` longtext NOT NULL, + `state` varchar(15) NOT NULL DEFAULT 'OPEN', + `admin_id` int NOT NULL DEFAULT 0, + CONSTRAINT `old_guilds_forums_comments_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `password_resets` ( + `email` varchar(255) NOT NULL, + `token` varchar(255) NOT NULL, + `created_at` timestamp, + CONSTRAINT `password_resets_email` PRIMARY KEY(`email`) +); +--> statement-breakpoint +CREATE TABLE `personal_access_tokens` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `tokenable_id` bigint unsigned NOT NULL, + `tokenable_type` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `token` varchar(64) NOT NULL, + `abilities` text, + `last_used_at` timestamp, + `expires_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `personal_access_tokens_id` PRIMARY KEY(`id`), + CONSTRAINT `personal_access_tokens_token_unique` UNIQUE(`token`) +); +--> statement-breakpoint +CREATE TABLE `pet_actions` ( + `ID` int AUTO_INCREMENT NOT NULL, + `pet_type` int NOT NULL, + `pet_name` varchar(32) NOT NULL, + `offspring_type` int NOT NULL DEFAULT -1, + `happy_actions` varchar(100) NOT NULL DEFAULT '', + `tired_actions` varchar(100) NOT NULL DEFAULT '', + `random_actions` varchar(100) NOT NULL DEFAULT '', + `can_swim` varchar(1) DEFAULT '0', + CONSTRAINT `pet_actions_ID` PRIMARY KEY(`ID`) +); +--> statement-breakpoint +CREATE TABLE `pet_breeds` ( + `race` int NOT NULL, + `color_one` int NOT NULL, + `color_two` int NOT NULL, + `has_color_one` varchar(1) NOT NULL DEFAULT '0', + `has_color_two` varchar(1) NOT NULL DEFAULT '0', + CONSTRAINT `pet_breeds_race_colorOne_colorTwo_hasColorOne_hasColorTwo` UNIQUE(`race`,`color_one`,`color_two`,`has_color_one`,`has_color_two`) +); +--> statement-breakpoint +CREATE TABLE `pet_commands_data` ( + `command_id` int NOT NULL, + `text` varchar(15) NOT NULL, + `required_level` int NOT NULL, + `reward_xp` int NOT NULL DEFAULT 5, + `cost_happyness` int NOT NULL DEFAULT 0, + `cost_energy` int NOT NULL DEFAULT 0, + CONSTRAINT `pet_commands_data_command_id` PRIMARY KEY(`command_id`) +); +--> statement-breakpoint +CREATE TABLE `polls` ( + `id` int AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL DEFAULT 'Hey! We''d appreciate it if you could take some time to answer these questions. It will help improve our hotel.', + `thanks_message` varchar(255) NOT NULL, + `reward_badge` varchar(10) NOT NULL DEFAULT '', + CONSTRAINT `polls_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `polls_answers` ( + `poll_id` int NOT NULL, + `user_id` int NOT NULL, + `question_id` int NOT NULL, + `answer` varchar(255) NOT NULL, + CONSTRAINT `polls_answers_pollId_userId_questionId` UNIQUE(`poll_id`,`user_id`,`question_id`) +); +--> statement-breakpoint +CREATE TABLE `polls_questions` ( + `id` int AUTO_INCREMENT NOT NULL, + `parent_id` int NOT NULL DEFAULT 0, + `poll_id` int NOT NULL, + `order` int NOT NULL, + `question` varchar(255) NOT NULL, + `type` int NOT NULL DEFAULT 2, + `min_selections` int NOT NULL DEFAULT 1, + `options` varchar(255) NOT NULL, + CONSTRAINT `polls_questions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_api_keys` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `key` varchar(64) NOT NULL, + `allowed_ips` varchar(255), + `permissions` longtext, + `rate_limit` int NOT NULL DEFAULT 300, + `last_used_at` timestamp, + `expires_at` timestamp, + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_api_keys_id` PRIMARY KEY(`id`), + CONSTRAINT `radio_api_keys_key_unique` UNIQUE(`key`) +); +--> statement-breakpoint +CREATE TABLE `radio_applications` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `real_name` varchar(255) NOT NULL, + `age` int NOT NULL, + `availability` text NOT NULL, + `experience` text, + `motivation` text NOT NULL, + `music_style` text, + `discord_username` varchar(255), + `rank_id` bigint unsigned, + `status` varchar(8) NOT NULL DEFAULT 'pending', + `approved_by` bigint unsigned, + `approved_at` timestamp, + `rejected_by` bigint unsigned, + `rejected_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_applications_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_auto_dj_playlist` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `artist` varchar(255), + `album` varchar(255), + `artwork_url` varchar(255), + `duration` int unsigned, + `play_count` int unsigned NOT NULL DEFAULT 0, + `last_played_at` timestamp, + `is_active` boolean NOT NULL DEFAULT true, + `sort_order` int unsigned NOT NULL DEFAULT 0, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_auto_dj_playlist_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_banners` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `image_path` varchar(255) NOT NULL, + `title` varchar(255), + `description` text, + `sort_order` int NOT NULL DEFAULT 0, + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_banners_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_contests` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `description` text, + `prize` varchar(255), + `start_date` timestamp, + `end_date` timestamp, + `winner_id` bigint unsigned, + `is_active` boolean NOT NULL DEFAULT true, + `created_by` bigint unsigned, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_contests_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_giveaways` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `description` text, + `prize` varchar(255), + `prize_amount` int NOT NULL DEFAULT 0, + `prize_currency` varchar(50), + `start_date` timestamp, + `end_date` timestamp, + `winner_id` bigint unsigned, + `is_active` boolean NOT NULL DEFAULT true, + `created_by` bigint unsigned, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_giveaways_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_history` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `show_name` varchar(255), + `started_at` timestamp NOT NULL, + `ended_at` timestamp, + `listeners_count` int NOT NULL DEFAULT 0, + `notes` text, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_history_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_listener_points` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `points` decimal(10,2) NOT NULL DEFAULT 0, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_listener_points_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_ranks` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `description` varchar(255), + `badge_code` varchar(255), + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_ranks_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_schedules` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `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), + `description` text, + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_schedules_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_shouts` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `message` text NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_shouts_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_song_plays` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `artist` varchar(255), + `album` varchar(255), + `artwork_url` varchar(255), + `duration` int unsigned, + `played_at` timestamp NOT NULL, + `dj_id` bigint unsigned, + `metadata` longtext, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_song_plays_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_song_requests` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned, + `song_title` varchar(255), + `artist` varchar(255), + `is_approved` boolean NOT NULL DEFAULT false, + `is_played` boolean NOT NULL DEFAULT false, + `votes` int NOT NULL DEFAULT 0, + `submitted_at` timestamp, + `played_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_song_requests_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `radio_song_votes` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `radio_song_votes_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `referrals` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `referred_user_id` bigint unsigned NOT NULL, + `referred_user_ip` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `referrals_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `room_models` ( + `name` varchar(100) NOT NULL, + `door_x` int NOT NULL, + `door_y` int NOT NULL, + `door_dir` int NOT NULL DEFAULT 2, + `heightmap` text NOT NULL, + `public_items` text NOT NULL, + `club_only` varchar(1) NOT NULL DEFAULT '0', + CONSTRAINT `room_models_name` PRIMARY KEY(`name`) +); +--> statement-breakpoint +CREATE TABLE `room_trade_log` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_one_id` int NOT NULL, + `user_two_id` int NOT NULL, + `user_one_ip` varchar(45) NOT NULL, + `user_two_ip` varchar(45) NOT NULL, + `timestamp` int NOT NULL, + `user_one_item_count` int NOT NULL, + `user_two_item_count` int NOT NULL, + CONSTRAINT `room_trade_log_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `room_trade_log_items` ( + `id` int NOT NULL, + `item_id` int NOT NULL, + `user_id` int NOT NULL, + CONSTRAINT `room_trade_log_items_id_itemId_userId` UNIQUE(`id`,`item_id`,`user_id`) +); +--> statement-breakpoint +CREATE TABLE `room_wordfilter` ( + `room_id` int NOT NULL, + `word` varchar(25) NOT NULL, + CONSTRAINT `room_wordfilter_roomId_word` UNIQUE(`room_id`,`word`) +); +--> statement-breakpoint +CREATE TABLE `rooms` ( + `id` int AUTO_INCREMENT NOT NULL, + `owner_id` int NOT NULL DEFAULT 0, + `owner_name` varchar(25) NOT NULL DEFAULT '', + `name` varchar(50) NOT NULL DEFAULT '', + `description` varchar(512) NOT NULL DEFAULT '', + `model` varchar(20) NOT NULL DEFAULT 'model_a', + `password` varchar(20) NOT NULL DEFAULT '', + `state` varchar(9) NOT NULL DEFAULT 'open', + `users` int NOT NULL DEFAULT 0, + `users_max` int NOT NULL DEFAULT 25, + `guild_id` int NOT NULL DEFAULT 0, + `category` int NOT NULL DEFAULT 1, + `score` int NOT NULL DEFAULT 0, + `paper_floor` varchar(50) NOT NULL DEFAULT '0.0', + `paper_wall` varchar(50) NOT NULL DEFAULT '0.0', + `paper_landscape` varchar(50) NOT NULL DEFAULT '0.0', + `thickness_wall` int NOT NULL DEFAULT 0, + `wall_height` int NOT NULL DEFAULT -1, + `thickness_floor` int NOT NULL DEFAULT 0, + `moodlight_data` varchar(254) NOT NULL DEFAULT '2,1,1,#000000,255;2,3,1,#000000,255;2,3,1,#000000,255;', + `tags` varchar(500) NOT NULL DEFAULT '', + `is_public` varchar(1) NOT NULL DEFAULT '0', + `is_staff_picked` varchar(1) NOT NULL DEFAULT '0', + `allow_other_pets` varchar(1) NOT NULL DEFAULT '0', + `allow_other_pets_eat` varchar(1) NOT NULL DEFAULT '0', + `allow_walkthrough` varchar(1) NOT NULL DEFAULT '1', + `allow_hidewall` varchar(1) NOT NULL DEFAULT '0', + `chat_mode` int NOT NULL DEFAULT 0, + `chat_weight` int NOT NULL DEFAULT 1, + `chat_speed` int NOT NULL DEFAULT 1, + `chat_hearing_distance` int NOT NULL DEFAULT 50, + `chat_protection` int NOT NULL DEFAULT 2, + `override_model` varchar(1) NOT NULL DEFAULT '0', + `who_can_mute` int NOT NULL DEFAULT 0, + `who_can_kick` int NOT NULL DEFAULT 0, + `who_can_ban` int NOT NULL DEFAULT 0, + `poll_id` int NOT NULL DEFAULT 0, + `roller_speed` int NOT NULL DEFAULT 4, + `promoted` varchar(1) NOT NULL DEFAULT '0', + `trade_mode` int NOT NULL DEFAULT 2, + `move_diagonally` varchar(1) NOT NULL DEFAULT '1', + `jukebox_active` varchar(1) NOT NULL DEFAULT '0', + `hidewired` varchar(1) NOT NULL DEFAULT '0', + `is_forsale` varchar(1) NOT NULL DEFAULT '0', + CONSTRAINT `rooms_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `sanction_levels` ( + `level` int NOT NULL, + `type` varchar(5) NOT NULL, + `hour_length` int NOT NULL, + `probation_days` int NOT NULL, + CONSTRAINT `sanction_levels_level` PRIMARY KEY(`level`) +); +--> statement-breakpoint +CREATE TABLE `sanctions` ( + `id` int AUTO_INCREMENT NOT NULL, + `habbo_id` int NOT NULL DEFAULT 0, + `sanction_level` int NOT NULL DEFAULT 0, + `probation_timestamp` int NOT NULL DEFAULT 0, + `reason` varchar(255) NOT NULL DEFAULT '', + `trade_locked_until` int NOT NULL DEFAULT 0, + `is_muted` tinyint NOT NULL DEFAULT 0, + `mute_duration` int NOT NULL DEFAULT 0, + CONSTRAINT `sanctions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `social_accounts` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `provider` varchar(255) NOT NULL, + `provider_id` varchar(255) NOT NULL, + `avatar` varchar(255), + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `social_accounts_id` PRIMARY KEY(`id`), + CONSTRAINT `social_accounts_provider_providerId` UNIQUE(`provider`,`provider_id`) +); +--> statement-breakpoint +CREATE TABLE `soundtracks` ( + `id` int AUTO_INCREMENT NOT NULL, + `code` varchar(32) NOT NULL, + `name` varchar(100) NOT NULL, + `author` varchar(50) NOT NULL, + `track` text NOT NULL, + `length` int NOT NULL DEFAULT 0, + CONSTRAINT `soundtracks_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `staff_activities` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `action` varchar(50) NOT NULL, + `target_type` varchar(100), + `target_id` bigint unsigned, + `description` text NOT NULL, + `ip_address` varchar(45), + `metadata` longtext, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp, + CONSTRAINT `staff_activities_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_cfh_categories` ( + `id` int AUTO_INCREMENT NOT NULL, + `name_internal` varchar(255), + `name_external` varchar(255), + CONSTRAINT `support_cfh_categories_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_cfh_topics` ( + `id` int AUTO_INCREMENT NOT NULL, + `category_id` int, + `name_internal` varchar(255), + `name_external` varchar(255), + `action` varchar(11) DEFAULT 'mods', + `ignore_target` varchar(1) NOT NULL DEFAULT '0', + `auto_reply` text, + `default_sanction` int NOT NULL DEFAULT 0, + CONSTRAINT `support_cfh_topics_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_issue_categories` ( + `id` int AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL DEFAULT 'PII', + CONSTRAINT `support_issue_categories_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_issue_presets` ( + `id` int AUTO_INCREMENT NOT NULL, + `category` int NOT NULL DEFAULT 1, + `name` varchar(100) NOT NULL DEFAULT '', + `message` varchar(300) NOT NULL DEFAULT '', + `reminder` varchar(100) NOT NULL, + `ban_for` int NOT NULL DEFAULT 0, + `mute_for` int NOT NULL DEFAULT 0, + CONSTRAINT `support_issue_presets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_presets` ( + `id` int AUTO_INCREMENT NOT NULL, + `type` varchar(4) NOT NULL DEFAULT 'user', + `preset` varchar(200) NOT NULL, + CONSTRAINT `support_presets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `support_tickets` ( + `id` int AUTO_INCREMENT NOT NULL, + `state` int NOT NULL DEFAULT 0, + `type` int NOT NULL DEFAULT 1, + `timestamp` int NOT NULL DEFAULT 0, + `score` int NOT NULL DEFAULT 0, + `sender_id` int NOT NULL DEFAULT 0, + `reported_id` int NOT NULL DEFAULT 0, + `room_id` int NOT NULL DEFAULT 0, + `mod_id` int NOT NULL DEFAULT 0, + `issue` varchar(500) NOT NULL DEFAULT '', + `category` int NOT NULL DEFAULT 0, + `group_id` int NOT NULL, + `thread_id` int NOT NULL, + `comment_id` int NOT NULL, + `photo_item_id` int NOT NULL DEFAULT -1, + CONSTRAINT `support_tickets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `taggables` ( + `tag_id` bigint unsigned NOT NULL, + `taggable_id` bigint unsigned NOT NULL, + `taggable_type` varchar(255) NOT NULL, + CONSTRAINT `taggables_tagId_taggableId_taggableType` UNIQUE(`tag_id`,`taggable_id`,`taggable_type`) +); +--> statement-breakpoint +CREATE TABLE `tags` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `background_color` varchar(10) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `tags_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `theme_scopes` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(100) NOT NULL, + `type` enum('global','site','module','route') NOT NULL, + `parent_id` bigint unsigned, + `site_domain` varchar(255), + `route_path` varchar(255), + `module_id` varchar(100), + `is_active` boolean NOT NULL DEFAULT true, + `sort_order` int NOT NULL DEFAULT 0, + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL, + CONSTRAINT `theme_scopes_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `theme_scope_values` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `scope_id` bigint unsigned NOT NULL, + `setting_key` varchar(100) NOT NULL, + `setting_val` varchar(255) NOT NULL, + `mode` enum('light','dark') NOT NULL DEFAULT 'light', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL, + CONSTRAINT `theme_scope_values_id` PRIMARY KEY(`id`), + CONSTRAINT `theme_scope_values_unique` UNIQUE(`scope_id`,`setting_key`,`mode`) +); +--> statement-breakpoint +CREATE TABLE `users` ( + `id` int AUTO_INCREMENT NOT NULL, + `username` varchar(25) NOT NULL, + `real_name` varchar(25) NOT NULL DEFAULT 'KREWS DEV', + `password` varchar(255) NOT NULL, + `mail` varchar(500), + `mail_verified` varchar(1) NOT NULL DEFAULT '0', + `account_created` int NOT NULL, + `account_day_of_birth` int NOT NULL DEFAULT 0, + `last_login` int NOT NULL DEFAULT 0, + `last_online` int NOT NULL DEFAULT 0, + `motto` varchar(127) NOT NULL DEFAULT '', + `look` varchar(256) NOT NULL DEFAULT '', + `gender` enum('M','F') NOT NULL DEFAULT 'M', + `rank` int NOT NULL DEFAULT 1, + `credits` int NOT NULL DEFAULT 2500, + `pixels` int NOT NULL DEFAULT 500, + `points` int NOT NULL DEFAULT 10, + `online` varchar(1) NOT NULL DEFAULT '0', + `auth_ticket` varchar(256) NOT NULL DEFAULT '', + `ip_register` varchar(255) NOT NULL, + `ip_current` varchar(255) NOT NULL, + `machine_id` varchar(64) NOT NULL DEFAULT '', + `home_room` int NOT NULL DEFAULT 0, + `secret_key` varchar(40), + `pincode` varchar(11), + `terms_accepted` boolean NOT NULL DEFAULT false, + `age_verified` boolean NOT NULL DEFAULT false, + `extra_rank` int, + `two_factor_secret` text, + `two_factor_recovery_codes` text, + `two_factor_confirmed_at` timestamp, + `website_jwt_version` int NOT NULL DEFAULT 0, + CONSTRAINT `users_id` PRIMARY KEY(`id`), + CONSTRAINT `users_username_unique` UNIQUE(`username`) +); +--> statement-breakpoint +CREATE TABLE `user_challenge_progress` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `challenge_id` bigint unsigned NOT NULL, + `progress` int NOT NULL DEFAULT 0, + `completed` boolean NOT NULL DEFAULT false, + `claimed` boolean NOT NULL DEFAULT false, + `completed_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `user_challenge_progress_id` PRIMARY KEY(`id`), + CONSTRAINT `user_challenge_progress_userId_challengeId` UNIQUE(`user_id`,`challenge_id`) +); +--> statement-breakpoint +CREATE TABLE `user_game_ranks` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `game` varchar(255) NOT NULL, + `rank_id` bigint unsigned NOT NULL, + `points` int NOT NULL DEFAULT 0, + `wins` int NOT NULL DEFAULT 0, + `games_played` int NOT NULL DEFAULT 0, + `win_streak` int NOT NULL DEFAULT 0, + `best_streak` int NOT NULL DEFAULT 0, + `last_played_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `user_game_ranks_id` PRIMARY KEY(`id`), + CONSTRAINT `user_game_ranks_userId_game` UNIQUE(`user_id`,`game`) +); +--> statement-breakpoint +CREATE TABLE `user_game_xp` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` bigint unsigned NOT NULL, + `game` varchar(255) NOT NULL, + `xp` int NOT NULL DEFAULT 0, + `level` int NOT NULL DEFAULT 1, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `user_game_xp_id` PRIMARY KEY(`id`), + CONSTRAINT `user_game_xp_userId_game` UNIQUE(`user_id`,`game`) +); +--> statement-breakpoint +CREATE TABLE `user_referrals` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `referrals_total` bigint unsigned NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `user_referrals_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `user_watch` ( + `id` int AUTO_INCREMENT NOT NULL, + `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, + CONSTRAINT `user_watch_id` PRIMARY KEY(`id`), + CONSTRAINT `user_watch_staffId_targetUserId` UNIQUE(`staff_id`,`target_user_id`) +); +--> statement-breakpoint +CREATE TABLE `users_achievements` ( + `user_id` int NOT NULL, + `achievement_name` varchar(255) NOT NULL, + `progress` int NOT NULL DEFAULT 1, + CONSTRAINT `users_achievements_userId_achievementName` UNIQUE(`user_id`,`achievement_name`) +); +--> statement-breakpoint +CREATE TABLE `users_achievements_queue` ( + `user_id` int NOT NULL, + `achievement_id` int NOT NULL, + `amount` int NOT NULL, + CONSTRAINT `users_achievements_queue_userId_achievementId` UNIQUE(`user_id`,`achievement_id`) +); +--> statement-breakpoint +CREATE TABLE `users_badges` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `slot_id` int NOT NULL DEFAULT 0, + `badge_code` varchar(32) NOT NULL, + CONSTRAINT `users_badges_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `users_clothing` ( + `user_id` int NOT NULL, + `clothing_id` int NOT NULL, + CONSTRAINT `users_clothing_userId_clothingId` UNIQUE(`user_id`,`clothing_id`) +); +--> statement-breakpoint +CREATE TABLE `users_currency` ( + `user_id` int NOT NULL, + `type` int NOT NULL, + `amount` int NOT NULL DEFAULT 0, + CONSTRAINT `users_currency_user_id_type_pk` PRIMARY KEY(`user_id`,`type`) +); +--> statement-breakpoint +CREATE TABLE `users_effects` ( + `user_id` int NOT NULL, + `effect` int NOT NULL, + `duration` int NOT NULL DEFAULT 86400, + `activation_timestamp` int NOT NULL DEFAULT -1, + `total` int NOT NULL DEFAULT 1, + CONSTRAINT `users_effects_userId_effect` UNIQUE(`user_id`,`effect`) +); +--> statement-breakpoint +CREATE TABLE `users_favorite_rooms` ( + `user_id` int NOT NULL, + `room_id` int NOT NULL, + CONSTRAINT `users_favorite_rooms_userId_roomId` UNIQUE(`user_id`,`room_id`) +); +--> statement-breakpoint +CREATE TABLE `users_pets` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `room_id` int NOT NULL, + `name` varchar(15) NOT NULL DEFAULT 'User Pet', + `race` int NOT NULL, + `type` int NOT NULL, + `color` varchar(6) NOT NULL, + `happyness` int NOT NULL DEFAULT 100, + `experience` int NOT NULL DEFAULT 0, + `energy` int NOT NULL DEFAULT 100, + `hunger` int NOT NULL DEFAULT 0, + `thirst` int NOT NULL DEFAULT 0, + `respect` int NOT NULL DEFAULT 0, + `created` int NOT NULL, + `x` int NOT NULL DEFAULT 0, + `y` int NOT NULL DEFAULT 0, + `z` double NOT NULL DEFAULT 0, + `rot` int NOT NULL DEFAULT 0, + `hair_style` int NOT NULL DEFAULT -1, + `hair_color` int NOT NULL DEFAULT 0, + `saddle` varchar(1) NOT NULL DEFAULT '0', + `ride` varchar(1) NOT NULL DEFAULT '0', + `mp_type` int NOT NULL DEFAULT 0, + `mp_color` int NOT NULL DEFAULT 0, + `mp_nose` int NOT NULL DEFAULT 0, + `mp_nose_color` tinyint NOT NULL DEFAULT 0, + `mp_eyes` int NOT NULL DEFAULT 0, + `mp_eyes_color` tinyint NOT NULL DEFAULT 0, + `mp_mouth` int NOT NULL DEFAULT 0, + `mp_mouth_color` tinyint NOT NULL DEFAULT 0, + `mp_death_timestamp` int NOT NULL DEFAULT 0, + `mp_breedable` varchar(1) NOT NULL DEFAULT '0', + `mp_allow_breed` varchar(1) NOT NULL DEFAULT '0', + `gnome_data` varchar(80) NOT NULL DEFAULT '', + `mp_is_dead` tinyint NOT NULL DEFAULT 0, + `saddle_item_id` int, + CONSTRAINT `users_pets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `users_recipes` ( + `user_id` int NOT NULL, + `recipe` int NOT NULL, + CONSTRAINT `users_recipes_userId_recipe` UNIQUE(`user_id`,`recipe`) +); +--> statement-breakpoint +CREATE TABLE `users_saved_searches` ( + `id` int AUTO_INCREMENT NOT NULL, + `search_code` varchar(255) NOT NULL, + `filter` varchar(255), + `user_id` int NOT NULL, + CONSTRAINT `users_saved_searches_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `users_settings` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `credits` int NOT NULL DEFAULT 0, + `achievement_score` int NOT NULL DEFAULT 0, + `daily_respect_points` int NOT NULL DEFAULT 3, + `daily_pet_respect_points` int NOT NULL DEFAULT 3, + `respects_given` int NOT NULL DEFAULT 0, + `respects_received` int NOT NULL DEFAULT 0, + `guild_id` int NOT NULL DEFAULT 0, + `can_change_name` varchar(1) NOT NULL DEFAULT '0', + `can_trade` varchar(1) NOT NULL DEFAULT '1', + `is_citizen` varchar(1) NOT NULL DEFAULT '0', + `citizen_level` int NOT NULL DEFAULT 0, + `helper_level` int NOT NULL DEFAULT 0, + `tradelock_amount` int NOT NULL DEFAULT 0, + `cfh_send` int NOT NULL DEFAULT 0, + `cfh_abusive` int NOT NULL DEFAULT 0, + `cfh_warnings` int NOT NULL DEFAULT 0, + `cfh_bans` int NOT NULL DEFAULT 0, + `block_following` varchar(1) NOT NULL DEFAULT '0', + `block_friendrequests` varchar(1) NOT NULL DEFAULT '0', + `block_roominvites` varchar(1) NOT NULL DEFAULT '0', + `volume_system` int NOT NULL DEFAULT 100, + `volume_furni` int NOT NULL DEFAULT 100, + `volume_trax` int NOT NULL DEFAULT 100, + `old_chat` varchar(1) NOT NULL DEFAULT '0', + `block_camera_follow` varchar(1) NOT NULL DEFAULT '0', + `chat_color` int NOT NULL DEFAULT 0, + `home_room` int NOT NULL DEFAULT 0, + `online_time` int NOT NULL DEFAULT 0, + `tags` varchar(255) NOT NULL DEFAULT 'Arcturus Emulator;', + `club_expire_timestamp` int NOT NULL DEFAULT 0, + `login_streak` int NOT NULL DEFAULT 0, + `rent_space_id` int NOT NULL DEFAULT 0, + `rent_space_endtime` int NOT NULL DEFAULT 0, + `hof_points` int NOT NULL DEFAULT 0, + `block_alerts` varchar(1) NOT NULL DEFAULT '0', + `talent_track_citizenship_level` int NOT NULL DEFAULT -1, + `talent_track_helpers_level` int NOT NULL DEFAULT -1, + `ignore_bots` varchar(1) NOT NULL DEFAULT '0', + `ignore_pets` varchar(1) NOT NULL DEFAULT '0', + `nux` varchar(1) NOT NULL DEFAULT '0', + `mute_end_timestamp` int NOT NULL DEFAULT 0, + `allow_name_change` varchar(1) NOT NULL DEFAULT '0', + `perk_trade` varchar(1) NOT NULL DEFAULT '0', + `forums_post_count` int DEFAULT 0, + `ui_flags` int NOT NULL DEFAULT 1, + `has_gotten_default_saved_searches` tinyint NOT NULL DEFAULT 0, + `hc_gifts_claimed` int DEFAULT 0, + `last_hc_payday` int DEFAULT 0, + `max_rooms` int DEFAULT 50, + `max_friends` int DEFAULT 300, + CONSTRAINT `users_settings_id` PRIMARY KEY(`id`), + CONSTRAINT `users_settings_user_id_unique` UNIQUE(`user_id`) +); +--> statement-breakpoint +CREATE TABLE `users_subscriptions` ( + `id` int unsigned AUTO_INCREMENT NOT NULL, + `user_id` int, + `subscription_type` varchar(255), + `timestamp_start` int unsigned, + `duration` int unsigned, + `active` tinyint DEFAULT 1, + CONSTRAINT `users_subscriptions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `users_target_offer_purchases` ( + `user_id` int NOT NULL, + `offer_id` int NOT NULL, + `state` int NOT NULL DEFAULT 0, + `amount` int NOT NULL DEFAULT 0, + `last_purchase` int NOT NULL DEFAULT 0, + CONSTRAINT `users_target_offer_purchases_userId_offerId` UNIQUE(`user_id`,`offer_id`) +); +--> statement-breakpoint +CREATE TABLE `users_wardrobe` ( + `id` int AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL DEFAULT 0, + `slot_id` int NOT NULL DEFAULT 0, + `look` varchar(256) NOT NULL, + `gender` varchar(1) NOT NULL DEFAULT 'F', + CONSTRAINT `users_wardrobe_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `voucher_history` ( + `id` int AUTO_INCREMENT NOT NULL, + `voucher_id` int NOT NULL, + `user_id` int NOT NULL, + `timestamp` int NOT NULL, + CONSTRAINT `voucher_history_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `vouchers` ( + `id` int AUTO_INCREMENT NOT NULL, + `code` varchar(10) NOT NULL, + `credits` int NOT NULL DEFAULT 0, + `points` int NOT NULL DEFAULT 0, + `points_type` int NOT NULL DEFAULT 0, + `catalog_item_id` int NOT NULL DEFAULT 0, + `amount` int NOT NULL DEFAULT 1, + `limit` int NOT NULL DEFAULT -1, + CONSTRAINT `vouchers_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_ads` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `image` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_ads_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_article_comments` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `article_id` bigint unsigned NOT NULL, + `user_id` int NOT NULL, + `comment` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_article_comments_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_article_reactions` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int unsigned NOT NULL, + `article_id` bigint unsigned NOT NULL, + `reaction` varchar(50) NOT NULL, + `active` boolean NOT NULL DEFAULT true, + CONSTRAINT `website_article_reactions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_articles` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `slug` varchar(255) NOT NULL, + `title` varchar(255) NOT NULL, + `short_story` varchar(255) NOT NULL, + `full_story` longtext NOT NULL, + `user_id` int, + `image` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + `status` varchar(20) NOT NULL DEFAULT 'published', + `publish_at` timestamp, + `published_at` timestamp, + CONSTRAINT `website_articles_id` PRIMARY KEY(`id`), + CONSTRAINT `website_articles_slug_unique` UNIQUE(`slug`) +); +--> statement-breakpoint +CREATE TABLE `website_badges` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `badge_key` varchar(255) NOT NULL, + `badge_name` varchar(255) NOT NULL, + `badge_description` text NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_badges_id` PRIMARY KEY(`id`), + CONSTRAINT `website_badges_badge_key_unique` UNIQUE(`badge_key`) +); +--> statement-breakpoint +CREATE TABLE `website_banners` ( + `id` int AUTO_INCREMENT NOT NULL, + `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), + `end_date` varchar(50), + CONSTRAINT `website_banners_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_beta_codes` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `code` varchar(255) NOT NULL, + `user_id` int, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_beta_codes_id` PRIMARY KEY(`id`), + CONSTRAINT `website_beta_codes_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `website_drawbadges` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `badge_path` varchar(255) NOT NULL, + `badge_url` varchar(255) NOT NULL, + `badge_name` varchar(255) NOT NULL, + `badge_desc` varchar(255) NOT NULL, + `published` boolean NOT NULL DEFAULT false, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_drawbadges_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_events` ( + `id` int AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `description` text NOT NULL, + `type_id` int NOT NULL, + `host_user_id` int NOT NULL, + `room_id` int, + `starts_at` datetime(3) NOT NULL, + `ends_at` datetime(3), + `max_players` int, + `is_recurring` int NOT NULL DEFAULT 0, + `recurrence_rule` varchar(255), + `status` varchar(20) NOT NULL DEFAULT 'draft', + `image` varchar(500), + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime(3) NOT NULL, + CONSTRAINT `website_events_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_event_prizes` ( + `id` int AUTO_INCREMENT NOT NULL, + `event_id` int NOT NULL, + `position` int NOT NULL DEFAULT 1, + `prize_type` varchar(20) NOT NULL DEFAULT 'badge', + `badge_code` varchar(50), + `credits` int NOT NULL DEFAULT 0, + `pixels` int NOT NULL DEFAULT 0, + `points` int NOT NULL DEFAULT 0, + `item_id` int, + `description` varchar(255) NOT NULL DEFAULT '', + CONSTRAINT `website_event_prizes_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_event_registrations` ( + `id` int AUTO_INCREMENT NOT NULL, + `event_id` int NOT NULL, + `user_id` int NOT NULL, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT `website_event_registrations_id` PRIMARY KEY(`id`), + CONSTRAINT `website_event_registrations_eventId_userId` UNIQUE(`event_id`,`user_id`) +); +--> statement-breakpoint +CREATE TABLE `website_event_types` ( + `id` int AUTO_INCREMENT NOT NULL, + `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` int NOT NULL DEFAULT 1, + `min_rank` int NOT NULL DEFAULT 0, + CONSTRAINT `website_event_types_id` PRIMARY KEY(`id`), + CONSTRAINT `website_event_types_slug_unique` UNIQUE(`slug`) +); +--> statement-breakpoint +CREATE TABLE `website_event_winners` ( + `id` int AUTO_INCREMENT NOT NULL, + `event_id` int NOT NULL, + `user_id` int NOT NULL, + `position` int NOT NULL DEFAULT 1, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT `website_event_winners_id` PRIMARY KEY(`id`), + CONSTRAINT `website_event_winners_eventId_userId` UNIQUE(`event_id`,`user_id`) +); +--> statement-breakpoint +CREATE TABLE `website_help_center_categories` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `content` text NOT NULL, + `position` int unsigned NOT NULL DEFAULT 1, + `image_url` varchar(255), + `button_text` varchar(255), + `button_url` varchar(255), + `button_color` varchar(16) NOT NULL DEFAULT '#eeb425', + `button_border_color` varchar(16) NOT NULL DEFAULT '#facc15', + `small_box` boolean NOT NULL DEFAULT false, + CONSTRAINT `website_help_center_categories_id` PRIMARY KEY(`id`), + CONSTRAINT `website_help_center_categories_name_unique` UNIQUE(`name`) +); +--> statement-breakpoint +CREATE TABLE `website_help_center_ticket_replies` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `ticket_id` bigint unsigned NOT NULL, + `user_id` int NOT NULL, + `content` text NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_help_center_ticket_replies_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_help_center_tickets` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int, + `category_id` bigint unsigned, + `title` varchar(255) NOT NULL, + `content` text NOT NULL, + `open` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_help_center_tickets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_housekeeping_permissions` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `permission` varchar(255) NOT NULL, + `min_rank` int NOT NULL, + `description` varchar(255), + `group_name` varchar(100), + `depends_on` varchar(255), + `updated_by` int, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_housekeeping_permissions_id` PRIMARY KEY(`id`), + CONSTRAINT `website_housekeeping_permissions_permission_unique` UNIQUE(`permission`) +); +--> statement-breakpoint +CREATE TABLE `website_installation` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `step` int unsigned DEFAULT 0, + `completed` boolean NOT NULL DEFAULT false, + `installation_key` varchar(255), + `user_ip` varchar(255), + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_installation_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_ip_blacklist` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `ip_address` varchar(255) NOT NULL, + `asn` varchar(255), + `blacklist_asn` boolean NOT NULL DEFAULT false, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_ip_blacklist_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_ip_whitelist` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `ip_address` varchar(255) NOT NULL, + `asn` varchar(255), + `whitelist_asn` boolean NOT NULL DEFAULT false, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_ip_whitelist_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_languages` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `country_code` varchar(8) NOT NULL, + `language` varchar(255) NOT NULL, + CONSTRAINT `website_languages_id` PRIMARY KEY(`id`), + CONSTRAINT `website_languages_country_code_unique` UNIQUE(`country_code`), + CONSTRAINT `website_languages_language_unique` UNIQUE(`language`) +); +--> statement-breakpoint +CREATE TABLE `website_login_logs` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `ip` varchar(45) NOT NULL, + `user_agent` varchar(512), + `created_at` timestamp, + CONSTRAINT `website_login_logs_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_maintenance_tasks` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int, + `task` varchar(255) NOT NULL, + `completed` boolean NOT NULL DEFAULT false, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_maintenance_tasks_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_open_positions` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `permission_id` int NOT NULL, + `description` text NOT NULL, + `apply_from` timestamp, + `apply_to` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_open_positions_id` PRIMARY KEY(`id`), + CONSTRAINT `website_open_positions_permission_id_unique` UNIQUE(`permission_id`) +); +--> statement-breakpoint +CREATE TABLE `website_paypal_transactions` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `transaction_id` varchar(255) NOT NULL, + `status` varchar(255), + `description` varchar(512), + `amount` double NOT NULL, + `currency` varchar(255) NOT NULL DEFAULT 'USD', + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_paypal_transactions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_permissions` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `permission` varchar(255) NOT NULL, + `min_rank` varchar(255) NOT NULL, + `description` varchar(255), + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_permissions_id` PRIMARY KEY(`id`), + CONSTRAINT `website_permissions_permission_unique` UNIQUE(`permission`) +); +--> statement-breakpoint +CREATE TABLE `website_polls` ( + `id` int AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `description` text, + `status` varchar(20) NOT NULL DEFAULT 'draft', + `show_results` int NOT NULL DEFAULT 1, + `multiple_choice` int NOT NULL DEFAULT 0, + `starts_at` datetime(3), + `ends_at` datetime(3), + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime(3) NOT NULL, + CONSTRAINT `website_polls_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_poll_questions` ( + `id` int AUTO_INCREMENT NOT NULL, + `poll_id` int NOT NULL, + `question` varchar(500) NOT NULL, + `type` varchar(20) NOT NULL DEFAULT 'single', + `sort_order` int NOT NULL DEFAULT 0, + `options` text NOT NULL, + CONSTRAINT `website_poll_questions_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_poll_votes` ( + `id` int AUTO_INCREMENT NOT NULL, + `question_id` int NOT NULL, + `user_id` int NOT NULL, + `answer` varchar(500) NOT NULL, + `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT `website_poll_votes_id` PRIMARY KEY(`id`), + CONSTRAINT `website_poll_votes_questionId_userId` UNIQUE(`question_id`,`user_id`) +); +--> statement-breakpoint +CREATE TABLE `website_rare_value_categories` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `badge` varchar(255) NOT NULL, + `priority` int unsigned NOT NULL DEFAULT 1, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_rare_value_categories_id` PRIMARY KEY(`id`), + CONSTRAINT `website_rare_value_categories_name_unique` UNIQUE(`name`) +); +--> statement-breakpoint +CREATE TABLE `website_rare_values` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `category_id` bigint unsigned NOT NULL, + `item_id` int, + `name` varchar(255) NOT NULL, + `credit_value` varchar(255), + `currency_value` varchar(255), + `currency_type` varchar(255) NOT NULL DEFAULT 'diamonds', + `furniture_icon` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_rare_values_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_rule_categories` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `description` varchar(255) NOT NULL, + `badge` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_rule_categories_id` PRIMARY KEY(`id`), + CONSTRAINT `website_rule_categories_name_unique` UNIQUE(`name`) +); +--> statement-breakpoint +CREATE TABLE `website_rules` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `category_id` bigint unsigned, + `paragraph` varchar(8) NOT NULL, + `rule` text NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_rules_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_settings` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `key` varchar(255) NOT NULL, + `value` text NOT NULL, + `comment` varchar(255), + CONSTRAINT `website_settings_id` PRIMARY KEY(`id`), + CONSTRAINT `website_settings_key_unique` UNIQUE(`key`) +); +--> statement-breakpoint +CREATE TABLE `website_shop_article_features` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `article_id` bigint unsigned NOT NULL, + `content` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_shop_article_features_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_shop_articles` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `website_shop_category_id` bigint unsigned, + `name` varchar(255) NOT NULL, + `info` varchar(255) NOT NULL, + `icon_url` varchar(255) NOT NULL, + `color` varchar(255) NOT NULL, + `costs` int unsigned NOT NULL, + `give_rank` int, + `is_giftable` boolean NOT NULL DEFAULT false, + `credits` int unsigned, + `duckets` int unsigned, + `diamonds` int unsigned, + `badges` varchar(255), + `furniture` longtext, + `position` int unsigned NOT NULL DEFAULT 0, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_shop_articles_id` PRIMARY KEY(`id`), + CONSTRAINT `website_shop_articles_name_unique` UNIQUE(`name`) +); +--> statement-breakpoint +CREATE TABLE `website_shop_categories` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `name` varchar(255) NOT NULL, + `description` text, + `icon` varchar(255), + `order` int NOT NULL DEFAULT 0, + CONSTRAINT `website_shop_categories_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_shop_vouchers` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `code` varchar(255) NOT NULL, + `max_uses` int unsigned NOT NULL DEFAULT 1, + `use_count` int unsigned NOT NULL DEFAULT 0, + `amount` int unsigned NOT NULL, + `expires_at` timestamp, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_shop_vouchers_id` PRIMARY KEY(`id`), + CONSTRAINT `website_shop_vouchers_code_unique` UNIQUE(`code`) +); +--> statement-breakpoint +CREATE TABLE `website_staff_applications` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `rank_id` int NOT NULL, + `content` text NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_staff_applications_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_teams` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `rank_name` varchar(255) NOT NULL, + `hidden_rank` boolean NOT NULL DEFAULT false, + `badge` varchar(255), + `job_description` varchar(255), + `staff_color` varchar(255) NOT NULL DEFAULT '#327fa8', + `staff_background` varchar(255) NOT NULL DEFAULT 'staff-bg.png', + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_teams_id` PRIMARY KEY(`id`), + CONSTRAINT `website_teams_rank_name_unique` UNIQUE(`rank_name`) +); +--> statement-breakpoint +CREATE TABLE `website_tickets` ( + `id` int AUTO_INCREMENT NOT NULL, + `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, + `closed_at` datetime, + `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NOT NULL, + CONSTRAINT `website_tickets_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_ticket_messages` ( + `id` int AUTO_INCREMENT NOT NULL, + `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, + CONSTRAINT `website_ticket_messages_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_ticket_templates` ( + `id` int AUTO_INCREMENT NOT NULL, + `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, + CONSTRAINT `website_ticket_templates_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_used_shop_vouchers` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id` int NOT NULL, + `voucher_id` bigint unsigned NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_used_shop_vouchers_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_user_guestbooks` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `profile_id` int NOT NULL, + `user_id` int NOT NULL, + `message` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_user_guestbooks_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `website_wordfilter` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `word` varchar(255) NOT NULL, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_wordfilter_id` PRIMARY KEY(`id`), + CONSTRAINT `website_wordfilter_word_unique` UNIQUE(`word`) +); +--> statement-breakpoint +CREATE TABLE `website_writeable_boxes` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `title` varchar(255) NOT NULL, + `icon` varchar(255), + `content` text NOT NULL, + `position` int NOT NULL DEFAULT 0, + `is_active` boolean NOT NULL DEFAULT true, + `created_at` timestamp, + `updated_at` timestamp, + CONSTRAINT `website_writeable_boxes_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +CREATE TABLE `youtube_playlists` ( + `item_id` int NOT NULL, + `playlist_id` varchar(255) NOT NULL, + `order` int NOT NULL, + CONSTRAINT `youtube_playlists_itemId_playlistId_order` UNIQUE(`item_id`,`playlist_id`,`order`) +); diff --git a/drizzle/drafts/meta/0000_snapshot.json b/drizzle/drafts/meta/0000_snapshot.json new file mode 100644 index 00000000..b2c5212a --- /dev/null +++ b/drizzle/drafts/meta/0000_snapshot.json @@ -0,0 +1,14087 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "29d0dd86-0b12-460e-a800-417774df6dbc", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "achievements": { + "name": "achievements", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ACH_'" + }, + "category": { + "name": "category", + "type": "varchar(12)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'identity'" + }, + "level": { + "name": "level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "reward_amount": { + "name": "reward_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "reward_type": { + "name": "reward_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 10 + }, + "progress_needed": { + "name": "progress_needed", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "achievements_name_level_pk": { + "name": "achievements_name_level_pk", + "columns": [ + "name", + "level" + ] + } + }, + "uniqueConstraints": { + "achievements_id_unique": { + "name": "achievements_id_unique", + "columns": [ + "id" + ] + } + }, + "checkConstraint": {} + }, + "achievements_talents": { + "name": "achievements_talents", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "type": { + "name": "type", + "type": "varchar(11)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'citizenship'" + }, + "level": { + "name": "level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "achievement_ids": { + "name": "achievement_ids", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "achievement_levels": { + "name": "achievement_levels", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "reward_furni": { + "name": "reward_furni", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "reward_perks": { + "name": "reward_perks", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "reward_badges": { + "name": "reward_badges", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "achievements_talents_id": { + "name": "achievements_talents_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "acl_model_permissions": { + "name": "acl_model_permissions", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "model_type": { + "name": "model_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Role'" + }, + "model_id": { + "name": "model_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permission_id": { + "name": "permission_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "acl_model_permissions_id": { + "name": "acl_model_permissions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "acl_model_roles": { + "name": "acl_model_roles", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "model_type": { + "name": "model_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'User'" + }, + "model_id": { + "name": "model_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role_id": { + "name": "role_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "acl_model_roles_id": { + "name": "acl_model_roles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "acl_permissions": { + "name": "acl_permissions", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "slug": { + "name": "slug", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "acl_permissions_id": { + "name": "acl_permissions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "acl_permissions_slug_unique": { + "name": "acl_permissions_slug_unique", + "columns": [ + "slug" + ] + } + }, + "checkConstraint": {} + }, + "acl_roles": { + "name": "acl_roles", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "slug": { + "name": "slug", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "acl_roles_id": { + "name": "acl_roles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "acl_roles_slug_unique": { + "name": "acl_roles_slug_unique", + "columns": [ + "slug" + ] + } + }, + "checkConstraint": {} + }, + "admin_audit_log": { + "name": "admin_audit_log", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "target": { + "name": "target", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "target_id": { + "name": "target_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "before": { + "name": "before", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "after": { + "name": "after", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "diff": { + "name": "diff", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "updated_at": { + "name": "updated_at", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "admin_audit_log_id": { + "name": "admin_audit_log_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "alert_logs": { + "name": "alert_logs", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "severity": { + "name": "severity", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "context": { + "name": "context", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sent_via_email": { + "name": "sent_via_email", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "sent_via_discord": { + "name": "sent_via_discord", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "alert_logs_id": { + "name": "alert_logs_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "bans": { + "name": "bans", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip": { + "name": "ip", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "machine_id": { + "name": "machine_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "user_staff_id": { + "name": "user_staff_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ban_expire": { + "name": "ban_expire", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "ban_reason": { + "name": "ban_reason", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "type": { + "name": "type", + "type": "enum('account','ip','machine','super')", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'account'" + }, + "cfh_topic": { + "name": "cfh_topic", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "bans_id": { + "name": "bans_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "bots": { + "name": "bots", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "name": { + "name": "name", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "motto": { + "name": "motto", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "figure": { + "name": "figure", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "gender": { + "name": "gender", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'M'" + }, + "x": { + "name": "x", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "y": { + "name": "y", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "z": { + "name": "z", + "type": "double", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rot": { + "name": "rot", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "chat_lines": { + "name": "chat_lines", + "type": "varchar(5112)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "chat_auto": { + "name": "chat_auto", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "chat_random": { + "name": "chat_random", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "chat_delay": { + "name": "chat_delay", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "dance": { + "name": "dance", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "freeroam": { + "name": "freeroam", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "type": { + "name": "type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'generic'" + }, + "effect": { + "name": "effect", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "bubble_id": { + "name": "bubble_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 31 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "bots_id": { + "name": "bots_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "calendar_campaigns": { + "name": "calendar_campaigns", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "start_timestamp": { + "name": "start_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total_days": { + "name": "total_days", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "lock_expired": { + "name": "lock_expired", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "calendar_campaigns_id": { + "name": "calendar_campaigns_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "calendar_rewards": { + "name": "calendar_rewards", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "campaign_id": { + "name": "campaign_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "product_name": { + "name": "product_name", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "custom_image": { + "name": "custom_image", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "pixels": { + "name": "pixels", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "badge": { + "name": "badge", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "subscription_type": { + "name": "subscription_type", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "subscription_days": { + "name": "subscription_days", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "calendar_rewards_id": { + "name": "calendar_rewards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "camera_web": { + "name": "camera_web", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "camera_web_id": { + "name": "camera_web_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_clothing": { + "name": "catalog_clothing", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(75)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "setid": { + "name": "setid", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_clothing_id": { + "name": "catalog_clothing_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_club_offers": { + "name": "catalog_club_offers", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "name": { + "name": "name", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "days": { + "name": "days", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "type": { + "name": "type", + "type": "varchar(19)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'HC'" + }, + "deal": { + "name": "deal", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "giftable": { + "name": "giftable", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_club_offers_id": { + "name": "catalog_club_offers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_featured_pages": { + "name": "catalog_featured_pages", + "columns": { + "slot_id": { + "name": "slot_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "varchar(70)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "caption": { + "name": "caption", + "type": "varchar(130)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "type": { + "name": "type", + "type": "varchar(12)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'page_name'" + }, + "expire_timestamp": { + "name": "expire_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "page_name": { + "name": "page_name", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "page_id": { + "name": "page_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "product_name": { + "name": "product_name", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_featured_pages_slot_id": { + "name": "catalog_featured_pages_slot_id", + "columns": [ + "slot_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_items": { + "name": "catalog_items", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "item_ids": { + "name": "item_ids", + "type": "varchar(666)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "catalog_name": { + "name": "catalog_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cost_credits": { + "name": "cost_credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 3 + }, + "cost_points": { + "name": "cost_points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "limited_stack": { + "name": "limited_stack", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "limited_sells": { + "name": "limited_sells", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "order_number": { + "name": "order_number", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "offer_id": { + "name": "offer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "song_id": { + "name": "song_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "extradata": { + "name": "extradata", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "haveOffer": { + "name": "haveOffer", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "club_only": { + "name": "club_only", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_items_id": { + "name": "catalog_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_items_bc": { + "name": "catalog_items_bc", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "item_ids": { + "name": "item_ids", + "type": "varchar(666)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "catalog_name": { + "name": "catalog_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order_number": { + "name": "order_number", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "extradata": { + "name": "extradata", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_items_bc_id": { + "name": "catalog_items_bc_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_items_limited": { + "name": "catalog_items_limited", + "columns": { + "catalog_item_id": { + "name": "catalog_item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "number": { + "name": "number", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "catalog_items_limited_catalogItemId_number": { + "name": "catalog_items_limited_catalogItemId_number", + "columns": [ + "catalog_item_id", + "number" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_pages": { + "name": "catalog_pages", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "parent_id": { + "name": "parent_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "caption_save": { + "name": "caption_save", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "caption": { + "name": "caption", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_layout": { + "name": "page_layout", + "type": "varchar(26)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'default_3x3'" + }, + "icon_color": { + "name": "icon_color", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "icon_image": { + "name": "icon_image", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "min_rank": { + "name": "min_rank", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "order_num": { + "name": "order_num", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "visible": { + "name": "visible", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "club_only": { + "name": "club_only", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "vip_only": { + "name": "vip_only", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "page_headline": { + "name": "page_headline", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "page_teaser": { + "name": "page_teaser", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "page_special": { + "name": "page_special", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text1": { + "name": "page_text1", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text2": { + "name": "page_text2", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text_details": { + "name": "page_text_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text_teaser": { + "name": "page_text_teaser", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "includes": { + "name": "includes", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_pages_id": { + "name": "catalog_pages_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_pages_bc": { + "name": "catalog_pages_bc", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "parent_id": { + "name": "parent_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "caption": { + "name": "caption", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_layout": { + "name": "page_layout", + "type": "varchar(26)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'default_3x3'" + }, + "icon_color": { + "name": "icon_color", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "icon_image": { + "name": "icon_image", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "order_num": { + "name": "order_num", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "visible": { + "name": "visible", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "page_headline": { + "name": "page_headline", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "page_teaser": { + "name": "page_teaser", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "page_special": { + "name": "page_special", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text1": { + "name": "page_text1", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text2": { + "name": "page_text2", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text_details": { + "name": "page_text_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_text_teaser": { + "name": "page_text_teaser", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_pages_bc_id": { + "name": "catalog_pages_bc_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "catalog_target_offers": { + "name": "catalog_target_offers", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "offer_code": { + "name": "offer_code", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "description": { + "name": "description", + "type": "varchar(2048)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "image": { + "name": "image", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_timestamp": { + "name": "end_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "purchase_limit": { + "name": "purchase_limit", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "catalog_item": { + "name": "catalog_item", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "vars": { + "name": "vars", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "catalog_target_offers_id": { + "name": "catalog_target_offers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "chatlogs_private": { + "name": "chatlogs_private", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_from_id": { + "name": "user_from_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_to_id": { + "name": "user_to_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "chatlogs_private_id": { + "name": "chatlogs_private_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "claimed_referral_logs": { + "name": "claimed_referral_logs", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "claimed_referral_logs_id": { + "name": "claimed_referral_logs_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "crafting_altars_recipes": { + "name": "crafting_altars_recipes", + "columns": { + "altar_id": { + "name": "altar_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipe_id": { + "name": "recipe_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "crafting_altars_recipes_altarId_recipeId": { + "name": "crafting_altars_recipes_altarId_recipeId", + "columns": [ + "altar_id", + "recipe_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "crafting_recipes": { + "name": "crafting_recipes", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_name": { + "name": "product_name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward": { + "name": "reward", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "achievement": { + "name": "achievement", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "secret": { + "name": "secret", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "limited": { + "name": "limited", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "remaining": { + "name": "remaining", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "crafting_recipes_productName": { + "name": "crafting_recipes_productName", + "columns": [ + "product_name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "crafting_recipes_id": { + "name": "crafting_recipes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "crafting_recipes_product_name_unique": { + "name": "crafting_recipes_product_name_unique", + "columns": [ + "product_name" + ] + } + }, + "checkConstraint": {} + }, + "daily_challenges": { + "name": "daily_challenges", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target": { + "name": "target", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward_currency": { + "name": "reward_currency", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'credits'" + }, + "reward_amount": { + "name": "reward_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "daily_challenges_id": { + "name": "daily_challenges_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "email_templates": { + "name": "email_templates", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "variables": { + "name": "variables", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "email_templates_id": { + "name": "email_templates_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "email_templates_name_unique": { + "name": "email_templates_name_unique", + "columns": [ + "name" + ] + } + }, + "checkConstraint": {} + }, + "emulator_errors": { + "name": "emulator_errors", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "version": { + "name": "version", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "build_hash": { + "name": "build_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Exception'" + }, + "stacktrace": { + "name": "stacktrace", + "type": "binary", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "emulator_errors_id": { + "name": "emulator_errors_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "emulator_settings": { + "name": "emulator_settings", + "columns": { + "key": { + "name": "key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "emulator_settings_key": { + "name": "emulator_settings_key", + "columns": [ + "key" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "emulator_texts": { + "name": "emulator_texts", + "columns": { + "key": { + "name": "key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "varchar(4096)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "emulator_texts_key": { + "name": "emulator_texts_key", + "columns": [ + "key" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_chat_messages": { + "name": "game_chat_messages", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "room_id": { + "name": "room_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'chat'" + }, + "reactions": { + "name": "reactions", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_chat_messages_id": { + "name": "game_chat_messages_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_invitations": { + "name": "game_invitations", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "from_user_id": { + "name": "from_user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to_user_id": { + "name": "to_user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "bet_amount": { + "name": "bet_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "bet_currency": { + "name": "bet_currency", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'credits'" + }, + "room_id": { + "name": "room_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_invitations_id": { + "name": "game_invitations_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_level_rewards": { + "name": "game_level_rewards", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "level": { + "name": "level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward_type": { + "name": "reward_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward_currency": { + "name": "reward_currency", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_amount": { + "name": "reward_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "claimed": { + "name": "claimed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_level_rewards_id": { + "name": "game_level_rewards_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_ranks": { + "name": "game_ranks", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'⭐'" + }, + "color": { + "name": "color", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#ffffff'" + }, + "min_points": { + "name": "min_points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "max_points": { + "name": "max_points", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tier": { + "name": "tier", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_ranks_id": { + "name": "game_ranks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "game_ranks_slug_unique": { + "name": "game_ranks_slug_unique", + "columns": [ + "slug" + ] + } + }, + "checkConstraint": {} + }, + "game_replays": { + "name": "game_replays", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "player1_id": { + "name": "player1_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "player2_id": { + "name": "player2_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "game_data": { + "name": "game_data", + "type": "longtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "moves": { + "name": "moves", + "type": "longtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "winner": { + "name": "winner", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "player1_score": { + "name": "player1_score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "player2_score": { + "name": "player2_score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_replays_id": { + "name": "game_replays_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_reward_claims": { + "name": "game_reward_claims", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "wins_required": { + "name": "wins_required", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "game_reward_claims_userId_game_winsRequired": { + "name": "game_reward_claims_userId_game_winsRequired", + "columns": [ + "user_id", + "game", + "wins_required" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_reward_claims_id": { + "name": "game_reward_claims_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_rooms": { + "name": "game_rooms", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "game": { + "name": "game", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "player1_id": { + "name": "player1_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "player2_id": { + "name": "player2_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "turn": { + "name": "turn", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "winner_id": { + "name": "winner_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'waiting'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_rooms_id": { + "name": "game_rooms_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "game_scores": { + "name": "game_scores", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "score": { + "name": "score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "game_scores_id": { + "name": "game_scores_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "gift_wrappers": { + "name": "gift_wrappers", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "sprite_id": { + "name": "sprite_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(7)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'wrapper'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "gift_wrappers_id": { + "name": "gift_wrappers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "groups_items": { + "name": "groups_items", + "columns": { + "type": { + "name": "type", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "firstvalue": { + "name": "firstvalue", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secondvalue": { + "name": "secondvalue", + "type": "varchar(2000)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "groups_items_id_type_pk": { + "name": "groups_items_id_type_pk", + "columns": [ + "id", + "type" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guild_forum_views": { + "name": "guild_forum_views", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "guild_forum_views_id": { + "name": "guild_forum_views_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guilds": { + "name": "guilds", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "description": { + "name": "description", + "type": "varchar(250)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rights": { + "name": "rights", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "color_one": { + "name": "color_one", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "color_two": { + "name": "color_two", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "badge": { + "name": "badge", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "date_created": { + "name": "date_created", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forum": { + "name": "forum", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "read_forum": { + "name": "read_forum", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'EVERYONE'" + }, + "post_messages": { + "name": "post_messages", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'EVERYONE'" + }, + "post_threads": { + "name": "post_threads", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'EVERYONE'" + }, + "mod_forum": { + "name": "mod_forum", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'ADMINS'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "guilds_id": { + "name": "guilds_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guilds_elements": { + "name": "guilds_elements", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "firstvalue": { + "name": "firstvalue", + "type": "varchar(300)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secondvalue": { + "name": "secondvalue", + "type": "varchar(300)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + } + }, + "indexes": { + "guilds_elements_id_type": { + "name": "guilds_elements_id_type", + "columns": [ + "id", + "type" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guilds_forums_comments": { + "name": "guilds_forums_comments", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "admin_id": { + "name": "admin_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "guilds_forums_comments_id": { + "name": "guilds_forums_comments_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guilds_forums_threads": { + "name": "guilds_forums_threads", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "opener_id": { + "name": "opener_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "subject": { + "name": "subject", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "posts_count": { + "name": "posts_count", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "pinned": { + "name": "pinned", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "locked": { + "name": "locked", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "admin_id": { + "name": "admin_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "guilds_forums_threads_id": { + "name": "guilds_forums_threads_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "guilds_members": { + "name": "guilds_members", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "level_id": { + "name": "level_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "member_since": { + "name": "member_since", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "guilds_members_id": { + "name": "guilds_members_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "hotelview_news": { + "name": "hotelview_news", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "button_text": { + "name": "button_text", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "button_type": { + "name": "button_type", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'web'" + }, + "button_link": { + "name": "button_link", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "hotelview_news_id": { + "name": "hotelview_news_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "items": { + "name": "items", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "item_id": { + "name": "item_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "wall_pos": { + "name": "wall_pos", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "x": { + "name": "x", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "y": { + "name": "y", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "z": { + "name": "z", + "type": "double", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rot": { + "name": "rot", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "extra_data": { + "name": "extra_data", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "wired_data": { + "name": "wired_data", + "type": "varchar(10000)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "limited_data": { + "name": "limited_data", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0:0'" + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "items_id": { + "name": "items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "items_base": { + "name": "items_base", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "sprite_id": { + "name": "sprite_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_name": { + "name": "public_name", + "type": "varchar(56)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "item_name": { + "name": "item_name", + "type": "varchar(70)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'s'" + }, + "width": { + "name": "width", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "length": { + "name": "length", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "stack_height": { + "name": "stack_height", + "type": "double", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_stack": { + "name": "allow_stack", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "allow_sit": { + "name": "allow_sit", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_lay": { + "name": "allow_lay", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_walk": { + "name": "allow_walk", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_gift": { + "name": "allow_gift", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "allow_trade": { + "name": "allow_trade", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "allow_recycle": { + "name": "allow_recycle", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_marketplace_sell": { + "name": "allow_marketplace_sell", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_inventory_stack": { + "name": "allow_inventory_stack", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "interaction_type": { + "name": "interaction_type", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'default'" + }, + "interaction_modes_count": { + "name": "interaction_modes_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "vending_ids": { + "name": "vending_ids", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "multiheight": { + "name": "multiheight", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "customparams": { + "name": "customparams", + "type": "varchar(25600)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "effect_id_male": { + "name": "effect_id_male", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "effect_id_female": { + "name": "effect_id_female", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "clothing_on_walk": { + "name": "clothing_on_walk", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "items_base_id": { + "name": "items_base_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "items_crackable": { + "name": "items_crackable", + "columns": { + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_name": { + "name": "item_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "count": { + "name": "count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prizes": { + "name": "prizes", + "type": "varchar(860)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'179:1'" + }, + "achievement_tick": { + "name": "achievement_tick", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "achievement_cracked": { + "name": "achievement_cracked", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_effect": { + "name": "required_effect", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "subscription_duration": { + "name": "subscription_duration", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_type": { + "name": "subscription_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "items_crackable_item_id": { + "name": "items_crackable_item_id", + "columns": [ + "item_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "items_highscore_data": { + "name": "items_highscore_data", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_ids": { + "name": "user_ids", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "score": { + "name": "score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_win": { + "name": "is_win", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "items_highscore_data_id": { + "name": "items_highscore_data_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "logs_furni_imports": { + "name": "logs_furni_imports", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "timestamp": { + "name": "timestamp", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "batch": { + "name": "batch", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "total_items": { + "name": "total_items", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "succeeded": { + "name": "succeeded", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "failed": { + "name": "failed", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "details": { + "name": "details", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rolled_back": { + "name": "rolled_back", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rolled_back_at": { + "name": "rolled_back_at", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rolled_back_by": { + "name": "rolled_back_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "logs_furni_imports_id": { + "name": "logs_furni_imports_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "logs_hc_payday": { + "name": "logs_hc_payday", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "timestamp": { + "name": "timestamp", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hc_streak": { + "name": "hc_streak", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_coins_spent": { + "name": "total_coins_spent", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_coins_spent": { + "name": "reward_coins_spent", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reward_streak": { + "name": "reward_streak", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "total_payout": { + "name": "total_payout", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "currency": { + "name": "currency", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "claimed": { + "name": "claimed", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "logs_hc_payday_id": { + "name": "logs_hc_payday_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "logs_shop_purchases": { + "name": "logs_shop_purchases", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "timestamp": { + "name": "timestamp", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "item_ids": { + "name": "item_ids", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "catalog_name": { + "name": "catalog_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost_credits": { + "name": "cost_credits", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cost_points": { + "name": "cost_points", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "logs_shop_purchases_id": { + "name": "logs_shop_purchases_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "marketplace_items": { + "name": "marketplace_items", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price": { + "name": "price", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sold_timestamp": { + "name": "sold_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "marketplace_items_id": { + "name": "marketplace_items_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "messenger_categories": { + "name": "messenger_categories", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "messenger_categories_id": { + "name": "messenger_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "messenger_friendrequests": { + "name": "messenger_friendrequests", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_to_id": { + "name": "user_to_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "user_from_id": { + "name": "user_from_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "messenger_friendrequests_id": { + "name": "messenger_friendrequests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "messenger_friendships": { + "name": "messenger_friendships", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_one_id": { + "name": "user_one_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "user_two_id": { + "name": "user_two_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "relation": { + "name": "relation", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "friends_since": { + "name": "friends_since", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "category": { + "name": "category", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "messenger_friendships_id": { + "name": "messenger_friendships_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "messenger_offline": { + "name": "messenger_offline", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "user_from_id": { + "name": "user_from_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "message": { + "name": "message", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "sended_on": { + "name": "sended_on", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "messenger_offline_id": { + "name": "messenger_offline_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "navigator_filter": { + "name": "navigator_filter", + "columns": { + "key": { + "name": "key", + "type": "varchar(11)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "field": { + "name": "field", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "compare": { + "name": "compare", + "type": "varchar(18)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "database_query": { + "name": "database_query", + "type": "varchar(1024)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "navigator_filter_key": { + "name": "navigator_filter_key", + "columns": [ + "key" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "navigator_flatcats": { + "name": "navigator_flatcats", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "min_rank": { + "name": "min_rank", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "caption_save": { + "name": "caption_save", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'caption_save'" + }, + "caption": { + "name": "caption", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "canTrade": { + "name": "canTrade", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "max_user_count": { + "name": "max_user_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "public": { + "name": "public", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "list_type": { + "name": "list_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "order_num": { + "name": "order_num", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "navigator_flatcats_id": { + "name": "navigator_flatcats_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "navigator_publiccats": { + "name": "navigator_publiccats", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Staff Picks'" + }, + "image": { + "name": "image", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "visible": { + "name": "visible", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "order_num": { + "name": "order_num", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "navigator_publiccats_id": { + "name": "navigator_publiccats_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "nux_gifts": { + "name": "nux_gifts", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "type": { + "name": "type", + "type": "varchar(4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'item'" + }, + "value": { + "name": "value", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "nux_gifts_id": { + "name": "nux_gifts_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "old_guilds_forums": { + "name": "old_guilds_forums", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject": { + "name": "subject", + "type": "mediumtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "longtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "varchar(15)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'OPEN'" + }, + "admin_id": { + "name": "admin_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "pinned": { + "name": "pinned", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "locked": { + "name": "locked", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "old_guilds_forums_id": { + "name": "old_guilds_forums_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "old_guilds_forums_comments": { + "name": "old_guilds_forums_comments", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "thread_id": { + "name": "thread_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "longtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "varchar(15)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'OPEN'" + }, + "admin_id": { + "name": "admin_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "old_guilds_forums_comments_id": { + "name": "old_guilds_forums_comments_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "password_resets": { + "name": "password_resets", + "columns": { + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "password_resets_email": { + "name": "password_resets_email", + "columns": [ + "email" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "personal_access_tokens": { + "name": "personal_access_tokens", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "tokenable_id": { + "name": "tokenable_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tokenable_type": { + "name": "tokenable_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "abilities": { + "name": "abilities", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "personal_access_tokens_id": { + "name": "personal_access_tokens_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "personal_access_tokens_token_unique": { + "name": "personal_access_tokens_token_unique", + "columns": [ + "token" + ] + } + }, + "checkConstraint": {} + }, + "pet_actions": { + "name": "pet_actions", + "columns": { + "ID": { + "name": "ID", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "pet_type": { + "name": "pet_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pet_name": { + "name": "pet_name", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "offspring_type": { + "name": "offspring_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "happy_actions": { + "name": "happy_actions", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "tired_actions": { + "name": "tired_actions", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "random_actions": { + "name": "random_actions", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "can_swim": { + "name": "can_swim", + "type": "varchar(1)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "pet_actions_ID": { + "name": "pet_actions_ID", + "columns": [ + "ID" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "pet_breeds": { + "name": "pet_breeds", + "columns": { + "race": { + "name": "race", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color_one": { + "name": "color_one", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color_two": { + "name": "color_two", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "has_color_one": { + "name": "has_color_one", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "has_color_two": { + "name": "has_color_two", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": { + "pet_breeds_race_colorOne_colorTwo_hasColorOne_hasColorTwo": { + "name": "pet_breeds_race_colorOne_colorTwo_hasColorOne_hasColorTwo", + "columns": [ + "race", + "color_one", + "color_two", + "has_color_one", + "has_color_two" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "pet_commands_data": { + "name": "pet_commands_data", + "columns": { + "command_id": { + "name": "command_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "text": { + "name": "text", + "type": "varchar(15)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "required_level": { + "name": "required_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward_xp": { + "name": "reward_xp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "cost_happyness": { + "name": "cost_happyness", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cost_energy": { + "name": "cost_energy", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "pet_commands_data_command_id": { + "name": "pet_commands_data_command_id", + "columns": [ + "command_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "polls": { + "name": "polls", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Hey! We''d appreciate it if you could take some time to answer these questions. It will help improve our hotel.'" + }, + "thanks_message": { + "name": "thanks_message", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reward_badge": { + "name": "reward_badge", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "polls_id": { + "name": "polls_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "polls_answers": { + "name": "polls_answers", + "columns": { + "poll_id": { + "name": "poll_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question_id": { + "name": "question_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answer": { + "name": "answer", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "polls_answers_pollId_userId_questionId": { + "name": "polls_answers_pollId_userId_questionId", + "columns": [ + "poll_id", + "user_id", + "question_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "polls_questions": { + "name": "polls_questions", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "parent_id": { + "name": "parent_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "poll_id": { + "name": "poll_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question": { + "name": "question", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 2 + }, + "min_selections": { + "name": "min_selections", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "options": { + "name": "options", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "polls_questions_id": { + "name": "polls_questions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_api_keys": { + "name": "radio_api_keys", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "allowed_ips": { + "name": "allowed_ips", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "permissions": { + "name": "permissions", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rate_limit": { + "name": "rate_limit", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 300 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_api_keys_id": { + "name": "radio_api_keys_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "radio_api_keys_key_unique": { + "name": "radio_api_keys_key_unique", + "columns": [ + "key" + ] + } + }, + "checkConstraint": {} + }, + "radio_applications": { + "name": "radio_applications", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "real_name": { + "name": "real_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "age": { + "name": "age", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "availability": { + "name": "availability", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "experience": { + "name": "experience", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "motivation": { + "name": "motivation", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "music_style": { + "name": "music_style", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "discord_username": { + "name": "discord_username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rank_id": { + "name": "rank_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "approved_by": { + "name": "approved_by", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rejected_by": { + "name": "rejected_by", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_applications_id": { + "name": "radio_applications_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_auto_dj_playlist": { + "name": "radio_auto_dj_playlist", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "artist": { + "name": "artist", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "album": { + "name": "album", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "artwork_url": { + "name": "artwork_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration": { + "name": "duration", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "play_count": { + "name": "play_count", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_played_at": { + "name": "last_played_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_auto_dj_playlist_id": { + "name": "radio_auto_dj_playlist_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_banners": { + "name": "radio_banners", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image_path": { + "name": "image_path", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_banners_id": { + "name": "radio_banners_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_contests": { + "name": "radio_contests", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prize": { + "name": "prize", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "winner_id": { + "name": "winner_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_by": { + "name": "created_by", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_contests_id": { + "name": "radio_contests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_giveaways": { + "name": "radio_giveaways", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prize": { + "name": "prize", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prize_amount": { + "name": "prize_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "prize_currency": { + "name": "prize_currency", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "winner_id": { + "name": "winner_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_by": { + "name": "created_by", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_giveaways_id": { + "name": "radio_giveaways_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_history": { + "name": "radio_history", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "show_name": { + "name": "show_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "listeners_count": { + "name": "listeners_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_history_id": { + "name": "radio_history_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_listener_points": { + "name": "radio_listener_points", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "points": { + "name": "points", + "type": "decimal(10,2)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_listener_points_id": { + "name": "radio_listener_points_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_ranks": { + "name": "radio_ranks", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "badge_code": { + "name": "badge_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_ranks_id": { + "name": "radio_ranks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_schedules": { + "name": "radio_schedules", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "day": { + "name": "day", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "start_time": { + "name": "start_time", + "type": "time", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_time": { + "name": "end_time", + "type": "time", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "show_name": { + "name": "show_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_schedules_id": { + "name": "radio_schedules_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_shouts": { + "name": "radio_shouts", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_shouts_id": { + "name": "radio_shouts_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_song_plays": { + "name": "radio_song_plays", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "artist": { + "name": "artist", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "album": { + "name": "album", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "artwork_url": { + "name": "artwork_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration": { + "name": "duration", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "played_at": { + "name": "played_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "dj_id": { + "name": "dj_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_song_plays_id": { + "name": "radio_song_plays_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_song_requests": { + "name": "radio_song_requests", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "song_title": { + "name": "song_title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "artist": { + "name": "artist", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_approved": { + "name": "is_approved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "is_played": { + "name": "is_played", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "votes": { + "name": "votes", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "submitted_at": { + "name": "submitted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "played_at": { + "name": "played_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_song_requests_id": { + "name": "radio_song_requests_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "radio_song_votes": { + "name": "radio_song_votes", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "radio_song_votes_id": { + "name": "radio_song_votes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "referrals": { + "name": "referrals", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "referred_user_id": { + "name": "referred_user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "referred_user_ip": { + "name": "referred_user_ip", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "referrals_id": { + "name": "referrals_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "room_models": { + "name": "room_models", + "columns": { + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "door_x": { + "name": "door_x", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "door_y": { + "name": "door_y", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "door_dir": { + "name": "door_dir", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 2 + }, + "heightmap": { + "name": "heightmap", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "public_items": { + "name": "public_items", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "club_only": { + "name": "club_only", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "room_models_name": { + "name": "room_models_name", + "columns": [ + "name" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "room_trade_log": { + "name": "room_trade_log", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_one_id": { + "name": "user_one_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_two_id": { + "name": "user_two_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_one_ip": { + "name": "user_one_ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_two_ip": { + "name": "user_two_ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_one_item_count": { + "name": "user_one_item_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_two_item_count": { + "name": "user_two_item_count", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "room_trade_log_id": { + "name": "room_trade_log_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "room_trade_log_items": { + "name": "room_trade_log_items", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "room_trade_log_items_id_itemId_userId": { + "name": "room_trade_log_items_id_itemId_userId", + "columns": [ + "id", + "item_id", + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "room_wordfilter": { + "name": "room_wordfilter", + "columns": { + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "word": { + "name": "word", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "room_wordfilter_roomId_word": { + "name": "room_wordfilter_roomId_word", + "columns": [ + "room_id", + "word" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "rooms": { + "name": "rooms", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "owner_id": { + "name": "owner_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "owner_name": { + "name": "owner_name", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "description": { + "name": "description", + "type": "varchar(512)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "model": { + "name": "model", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'model_a'" + }, + "password": { + "name": "password", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "state": { + "name": "state", + "type": "varchar(9)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'open'" + }, + "users": { + "name": "users", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "users_max": { + "name": "users_max", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 25 + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "category": { + "name": "category", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "score": { + "name": "score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "paper_floor": { + "name": "paper_floor", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0.0'" + }, + "paper_wall": { + "name": "paper_wall", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0.0'" + }, + "paper_landscape": { + "name": "paper_landscape", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0.0'" + }, + "thickness_wall": { + "name": "thickness_wall", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "wall_height": { + "name": "wall_height", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "thickness_floor": { + "name": "thickness_floor", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "moodlight_data": { + "name": "moodlight_data", + "type": "varchar(254)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'2,1,1,#000000,255;2,3,1,#000000,255;2,3,1,#000000,255;'" + }, + "tags": { + "name": "tags", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "is_public": { + "name": "is_public", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "is_staff_picked": { + "name": "is_staff_picked", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "allow_other_pets": { + "name": "allow_other_pets", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "allow_other_pets_eat": { + "name": "allow_other_pets_eat", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "allow_walkthrough": { + "name": "allow_walkthrough", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "allow_hidewall": { + "name": "allow_hidewall", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "chat_mode": { + "name": "chat_mode", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "chat_weight": { + "name": "chat_weight", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "chat_speed": { + "name": "chat_speed", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "chat_hearing_distance": { + "name": "chat_hearing_distance", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 50 + }, + "chat_protection": { + "name": "chat_protection", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 2 + }, + "override_model": { + "name": "override_model", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "who_can_mute": { + "name": "who_can_mute", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "who_can_kick": { + "name": "who_can_kick", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "who_can_ban": { + "name": "who_can_ban", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "poll_id": { + "name": "poll_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "roller_speed": { + "name": "roller_speed", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 4 + }, + "promoted": { + "name": "promoted", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "trade_mode": { + "name": "trade_mode", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 2 + }, + "move_diagonally": { + "name": "move_diagonally", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "jukebox_active": { + "name": "jukebox_active", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "hidewired": { + "name": "hidewired", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "is_forsale": { + "name": "is_forsale", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "rooms_id": { + "name": "rooms_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "sanction_levels": { + "name": "sanction_levels", + "columns": { + "level": { + "name": "level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hour_length": { + "name": "hour_length", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "probation_days": { + "name": "probation_days", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "sanction_levels_level": { + "name": "sanction_levels_level", + "columns": [ + "level" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "sanctions": { + "name": "sanctions", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "habbo_id": { + "name": "habbo_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "sanction_level": { + "name": "sanction_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "probation_timestamp": { + "name": "probation_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "reason": { + "name": "reason", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "trade_locked_until": { + "name": "trade_locked_until", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_muted": { + "name": "is_muted", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mute_duration": { + "name": "mute_duration", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "sanctions_id": { + "name": "sanctions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "social_accounts": { + "name": "social_accounts", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar": { + "name": "avatar", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "social_accounts_provider_providerId": { + "name": "social_accounts_provider_providerId", + "columns": [ + "provider", + "provider_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "social_accounts_id": { + "name": "social_accounts_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "soundtracks": { + "name": "soundtracks", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "author": { + "name": "author", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "track": { + "name": "track", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "length": { + "name": "length", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "soundtracks_id": { + "name": "soundtracks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "staff_activities": { + "name": "staff_activities", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_type": { + "name": "target_type", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_id": { + "name": "target_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(45)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "staff_activities_id": { + "name": "staff_activities_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_cfh_categories": { + "name": "support_cfh_categories", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name_internal": { + "name": "name_internal", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name_external": { + "name": "name_external", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_cfh_categories_id": { + "name": "support_cfh_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_cfh_topics": { + "name": "support_cfh_topics", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "category_id": { + "name": "category_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name_internal": { + "name": "name_internal", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name_external": { + "name": "name_external", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "varchar(11)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'mods'" + }, + "ignore_target": { + "name": "ignore_target", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "auto_reply": { + "name": "auto_reply", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_sanction": { + "name": "default_sanction", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_cfh_topics_id": { + "name": "support_cfh_topics_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_issue_categories": { + "name": "support_issue_categories", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'PII'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_issue_categories_id": { + "name": "support_issue_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_issue_presets": { + "name": "support_issue_presets", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "category": { + "name": "category", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "message": { + "name": "message", + "type": "varchar(300)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "reminder": { + "name": "reminder", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ban_for": { + "name": "ban_for", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mute_for": { + "name": "mute_for", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_issue_presets_id": { + "name": "support_issue_presets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_presets": { + "name": "support_presets", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "type": { + "name": "type", + "type": "varchar(4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'user'" + }, + "preset": { + "name": "preset", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_presets_id": { + "name": "support_presets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "support_tickets": { + "name": "support_tickets", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "type": { + "name": "type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "score": { + "name": "score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "sender_id": { + "name": "sender_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "reported_id": { + "name": "reported_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mod_id": { + "name": "mod_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "issue": { + "name": "issue", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "category": { + "name": "category", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "group_id": { + "name": "group_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "thread_id": { + "name": "thread_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment_id": { + "name": "comment_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "photo_item_id": { + "name": "photo_item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "support_tickets_id": { + "name": "support_tickets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "taggables": { + "name": "taggables", + "columns": { + "tag_id": { + "name": "tag_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "taggable_id": { + "name": "taggable_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "taggable_type": { + "name": "taggable_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "taggables_tagId_taggableId_taggableType": { + "name": "taggables_tagId_taggableId_taggableType", + "columns": [ + "tag_id", + "taggable_id", + "taggable_type" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "tags": { + "name": "tags", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "background_color": { + "name": "background_color", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "tags_id": { + "name": "tags_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "theme_scopes": { + "name": "theme_scopes", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "enum('global','site','module','route')", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parent_id": { + "name": "parent_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "site_domain": { + "name": "site_domain", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "route_path": { + "name": "route_path", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "module_id": { + "name": "module_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "theme_scopes_id": { + "name": "theme_scopes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "theme_scope_values": { + "name": "theme_scope_values", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "scope_id": { + "name": "scope_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "setting_key": { + "name": "setting_key", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "setting_val": { + "name": "setting_val", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mode": { + "name": "mode", + "type": "enum('light','dark')", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'light'" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "theme_scope_values_unique": { + "name": "theme_scope_values_unique", + "columns": [ + "scope_id", + "setting_key", + "mode" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "theme_scope_values_id": { + "name": "theme_scope_values_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "username": { + "name": "username", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "real_name": { + "name": "real_name", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'KREWS DEV'" + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mail": { + "name": "mail", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "mail_verified": { + "name": "mail_verified", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "account_created": { + "name": "account_created", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "account_day_of_birth": { + "name": "account_day_of_birth", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_login": { + "name": "last_login", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_online": { + "name": "last_online", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "motto": { + "name": "motto", + "type": "varchar(127)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "look": { + "name": "look", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "gender": { + "name": "gender", + "type": "enum('M','F')", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'M'" + }, + "rank": { + "name": "rank", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 2500 + }, + "pixels": { + "name": "pixels", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 500 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 10 + }, + "online": { + "name": "online", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "auth_ticket": { + "name": "auth_ticket", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "ip_register": { + "name": "ip_register", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_current": { + "name": "ip_current", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "machine_id": { + "name": "machine_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "home_room": { + "name": "home_room", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "secret_key": { + "name": "secret_key", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pincode": { + "name": "pincode", + "type": "varchar(11)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terms_accepted": { + "name": "terms_accepted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "age_verified": { + "name": "age_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "extra_rank": { + "name": "extra_rank", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "two_factor_secret": { + "name": "two_factor_secret", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "two_factor_recovery_codes": { + "name": "two_factor_recovery_codes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "two_factor_confirmed_at": { + "name": "two_factor_confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "website_jwt_version": { + "name": "website_jwt_version", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_id": { + "name": "users_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "users_username_unique": { + "name": "users_username_unique", + "columns": [ + "username" + ] + } + }, + "checkConstraint": {} + }, + "user_challenge_progress": { + "name": "user_challenge_progress", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "challenge_id": { + "name": "challenge_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "progress": { + "name": "progress", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "completed": { + "name": "completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "claimed": { + "name": "claimed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_challenge_progress_userId_challengeId": { + "name": "user_challenge_progress_userId_challengeId", + "columns": [ + "user_id", + "challenge_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_challenge_progress_id": { + "name": "user_challenge_progress_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_game_ranks": { + "name": "user_game_ranks", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rank_id": { + "name": "rank_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "wins": { + "name": "wins", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "games_played": { + "name": "games_played", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "win_streak": { + "name": "win_streak", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "best_streak": { + "name": "best_streak", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_played_at": { + "name": "last_played_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_game_ranks_userId_game": { + "name": "user_game_ranks_userId_game", + "columns": [ + "user_id", + "game" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_game_ranks_id": { + "name": "user_game_ranks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_game_xp": { + "name": "user_game_xp", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "game": { + "name": "game", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "xp": { + "name": "xp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "level": { + "name": "level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_game_xp_userId_game": { + "name": "user_game_xp_userId_game", + "columns": [ + "user_id", + "game" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_game_xp_id": { + "name": "user_game_xp_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_referrals": { + "name": "user_referrals", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "referrals_total": { + "name": "referrals_total", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_referrals_id": { + "name": "user_referrals_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "user_watch": { + "name": "user_watch", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "staff_id": { + "name": "staff_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_user_id": { + "name": "target_user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "user_watch_staffId_targetUserId": { + "name": "user_watch_staffId_targetUserId", + "columns": [ + "staff_id", + "target_user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_watch_id": { + "name": "user_watch_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_achievements": { + "name": "users_achievements", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "achievement_name": { + "name": "achievement_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "progress": { + "name": "progress", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": { + "users_achievements_userId_achievementName": { + "name": "users_achievements_userId_achievementName", + "columns": [ + "user_id", + "achievement_name" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_achievements_queue": { + "name": "users_achievements_queue", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "achievement_id": { + "name": "achievement_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_achievements_queue_userId_achievementId": { + "name": "users_achievements_queue_userId_achievementId", + "columns": [ + "user_id", + "achievement_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_badges": { + "name": "users_badges", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "slot_id": { + "name": "slot_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "badge_code": { + "name": "badge_code", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_badges_id": { + "name": "users_badges_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_clothing": { + "name": "users_clothing", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "clothing_id": { + "name": "clothing_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_clothing_userId_clothingId": { + "name": "users_clothing_userId_clothingId", + "columns": [ + "user_id", + "clothing_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_currency": { + "name": "users_currency", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_currency_user_id_type_pk": { + "name": "users_currency_user_id_type_pk", + "columns": [ + "user_id", + "type" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_effects": { + "name": "users_effects", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "effect": { + "name": "effect", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration": { + "name": "duration", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 86400 + }, + "activation_timestamp": { + "name": "activation_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "total": { + "name": "total", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": { + "users_effects_userId_effect": { + "name": "users_effects_userId_effect", + "columns": [ + "user_id", + "effect" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_favorite_rooms": { + "name": "users_favorite_rooms", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_favorite_rooms_userId_roomId": { + "name": "users_favorite_rooms_userId_roomId", + "columns": [ + "user_id", + "room_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_pets": { + "name": "users_pets", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(15)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'User Pet'" + }, + "race": { + "name": "race", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "happyness": { + "name": "happyness", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "experience": { + "name": "experience", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "energy": { + "name": "energy", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "hunger": { + "name": "hunger", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "thirst": { + "name": "thirst", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "respect": { + "name": "respect", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created": { + "name": "created", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "x": { + "name": "x", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "y": { + "name": "y", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "z": { + "name": "z", + "type": "double", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rot": { + "name": "rot", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "hair_style": { + "name": "hair_style", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "hair_color": { + "name": "hair_color", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "saddle": { + "name": "saddle", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "ride": { + "name": "ride", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "mp_type": { + "name": "mp_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_color": { + "name": "mp_color", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_nose": { + "name": "mp_nose", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_nose_color": { + "name": "mp_nose_color", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_eyes": { + "name": "mp_eyes", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_eyes_color": { + "name": "mp_eyes_color", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_mouth": { + "name": "mp_mouth", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_mouth_color": { + "name": "mp_mouth_color", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_death_timestamp": { + "name": "mp_death_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "mp_breedable": { + "name": "mp_breedable", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "mp_allow_breed": { + "name": "mp_allow_breed", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "gnome_data": { + "name": "gnome_data", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "mp_is_dead": { + "name": "mp_is_dead", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "saddle_item_id": { + "name": "saddle_item_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_pets_id": { + "name": "users_pets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_recipes": { + "name": "users_recipes", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipe": { + "name": "recipe", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_recipes_userId_recipe": { + "name": "users_recipes_userId_recipe", + "columns": [ + "user_id", + "recipe" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_saved_searches": { + "name": "users_saved_searches", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "search_code": { + "name": "search_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "filter": { + "name": "filter", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_saved_searches_id": { + "name": "users_saved_searches_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_settings": { + "name": "users_settings", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "achievement_score": { + "name": "achievement_score", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "daily_respect_points": { + "name": "daily_respect_points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 3 + }, + "daily_pet_respect_points": { + "name": "daily_pet_respect_points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 3 + }, + "respects_given": { + "name": "respects_given", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "respects_received": { + "name": "respects_received", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "guild_id": { + "name": "guild_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "can_change_name": { + "name": "can_change_name", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "can_trade": { + "name": "can_trade", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1'" + }, + "is_citizen": { + "name": "is_citizen", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "citizen_level": { + "name": "citizen_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "helper_level": { + "name": "helper_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tradelock_amount": { + "name": "tradelock_amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cfh_send": { + "name": "cfh_send", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cfh_abusive": { + "name": "cfh_abusive", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cfh_warnings": { + "name": "cfh_warnings", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "cfh_bans": { + "name": "cfh_bans", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "block_following": { + "name": "block_following", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "block_friendrequests": { + "name": "block_friendrequests", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "block_roominvites": { + "name": "block_roominvites", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "volume_system": { + "name": "volume_system", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "volume_furni": { + "name": "volume_furni", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "volume_trax": { + "name": "volume_trax", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 100 + }, + "old_chat": { + "name": "old_chat", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "block_camera_follow": { + "name": "block_camera_follow", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "chat_color": { + "name": "chat_color", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "home_room": { + "name": "home_room", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "online_time": { + "name": "online_time", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "tags": { + "name": "tags", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Arcturus Emulator;'" + }, + "club_expire_timestamp": { + "name": "club_expire_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "login_streak": { + "name": "login_streak", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rent_space_id": { + "name": "rent_space_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "rent_space_endtime": { + "name": "rent_space_endtime", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "hof_points": { + "name": "hof_points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "block_alerts": { + "name": "block_alerts", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "talent_track_citizenship_level": { + "name": "talent_track_citizenship_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "talent_track_helpers_level": { + "name": "talent_track_helpers_level", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + }, + "ignore_bots": { + "name": "ignore_bots", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "ignore_pets": { + "name": "ignore_pets", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "nux": { + "name": "nux", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "mute_end_timestamp": { + "name": "mute_end_timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "allow_name_change": { + "name": "allow_name_change", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "perk_trade": { + "name": "perk_trade", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'0'" + }, + "forums_post_count": { + "name": "forums_post_count", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "ui_flags": { + "name": "ui_flags", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "has_gotten_default_saved_searches": { + "name": "has_gotten_default_saved_searches", + "type": "tinyint", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "hc_gifts_claimed": { + "name": "hc_gifts_claimed", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "last_hc_payday": { + "name": "last_hc_payday", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "max_rooms": { + "name": "max_rooms", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 50 + }, + "max_friends": { + "name": "max_friends", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 300 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_settings_id": { + "name": "users_settings_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "users_settings_user_id_unique": { + "name": "users_settings_user_id_unique", + "columns": [ + "user_id" + ] + } + }, + "checkConstraint": {} + }, + "users_subscriptions": { + "name": "users_subscriptions", + "columns": { + "id": { + "name": "id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_type": { + "name": "subscription_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timestamp_start": { + "name": "timestamp_start", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duration": { + "name": "duration", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active": { + "name": "active", + "type": "tinyint", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_subscriptions_id": { + "name": "users_subscriptions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_target_offer_purchases": { + "name": "users_target_offer_purchases", + "columns": { + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "offer_id": { + "name": "offer_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_purchase": { + "name": "last_purchase", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "users_target_offer_purchases_userId_offerId": { + "name": "users_target_offer_purchases_userId_offerId", + "columns": [ + "user_id", + "offer_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_wardrobe": { + "name": "users_wardrobe", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "slot_id": { + "name": "slot_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "look": { + "name": "look", + "type": "varchar(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "gender": { + "name": "gender", + "type": "varchar(1)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'F'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_wardrobe_id": { + "name": "users_wardrobe_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "voucher_history": { + "name": "voucher_history", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "voucher_id": { + "name": "voucher_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "voucher_history_id": { + "name": "voucher_history_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "vouchers": { + "name": "vouchers", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points_type": { + "name": "points_type", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount": { + "name": "amount", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "limit": { + "name": "limit", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": -1 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "vouchers_id": { + "name": "vouchers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_ads": { + "name": "website_ads", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_ads_id": { + "name": "website_ads_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_article_comments": { + "name": "website_article_comments", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "article_id": { + "name": "article_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment": { + "name": "comment", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_article_comments_id": { + "name": "website_article_comments_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_article_reactions": { + "name": "website_article_reactions", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "article_id": { + "name": "article_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reaction": { + "name": "reaction", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_article_reactions_id": { + "name": "website_article_reactions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_articles": { + "name": "website_articles", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "short_story": { + "name": "short_story", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "full_story": { + "name": "full_story", + "type": "longtext", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'published'" + }, + "publish_at": { + "name": "publish_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_articles_id": { + "name": "website_articles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_articles_slug_unique": { + "name": "website_articles_slug_unique", + "columns": [ + "slug" + ] + } + }, + "checkConstraint": {} + }, + "website_badges": { + "name": "website_badges", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "badge_key": { + "name": "badge_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_name": { + "name": "badge_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_description": { + "name": "badge_description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_badges_id": { + "name": "website_badges_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_badges_badge_key_unique": { + "name": "website_badges_badge_key_unique", + "columns": [ + "badge_key" + ] + } + }, + "checkConstraint": {} + }, + "website_banners": { + "name": "website_banners", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "subtitle": { + "name": "subtitle", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "link": { + "name": "link", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "color": { + "name": "color", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "is_active": { + "name": "is_active", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "start_date": { + "name": "start_date", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_banners_id": { + "name": "website_banners_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_beta_codes": { + "name": "website_beta_codes", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_beta_codes_id": { + "name": "website_beta_codes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_beta_codes_code_unique": { + "name": "website_beta_codes_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "website_drawbadges": { + "name": "website_drawbadges", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_path": { + "name": "badge_path", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_url": { + "name": "badge_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_name": { + "name": "badge_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge_desc": { + "name": "badge_desc", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published": { + "name": "published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_drawbadges_id": { + "name": "website_drawbadges_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_events": { + "name": "website_events", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type_id": { + "name": "type_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "host_user_id": { + "name": "host_user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "room_id": { + "name": "room_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "starts_at": { + "name": "starts_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ends_at": { + "name": "ends_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "max_players": { + "name": "max_players", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_recurring": { + "name": "is_recurring", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "recurrence_rule": { + "name": "recurrence_rule", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "image": { + "name": "image", + "type": "varchar(500)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_events_id": { + "name": "website_events_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_event_prizes": { + "name": "website_event_prizes", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "event_id": { + "name": "event_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "prize_type": { + "name": "prize_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'badge'" + }, + "badge_code": { + "name": "badge_code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credits": { + "name": "credits", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "pixels": { + "name": "pixels", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "points": { + "name": "points", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_event_prizes_id": { + "name": "website_event_prizes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_event_registrations": { + "name": "website_event_registrations", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "event_id": { + "name": "event_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "website_event_registrations_eventId_userId": { + "name": "website_event_registrations_eventId_userId", + "columns": [ + "event_id", + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_event_registrations_id": { + "name": "website_event_registrations_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_event_types": { + "name": "website_event_types", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "color": { + "name": "color", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#3b82f6'" + }, + "icon": { + "name": "icon", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'calendar'" + }, + "is_active": { + "name": "is_active", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "min_rank": { + "name": "min_rank", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_event_types_id": { + "name": "website_event_types_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_event_types_slug_unique": { + "name": "website_event_types_slug_unique", + "columns": [ + "slug" + ] + } + }, + "checkConstraint": {} + }, + "website_event_winners": { + "name": "website_event_winners", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "event_id": { + "name": "event_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "website_event_winners_eventId_userId": { + "name": "website_event_winners_eventId_userId", + "columns": [ + "event_id", + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_event_winners_id": { + "name": "website_event_winners_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_help_center_categories": { + "name": "website_help_center_categories", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "image_url": { + "name": "image_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "button_text": { + "name": "button_text", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "button_url": { + "name": "button_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "button_color": { + "name": "button_color", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#eeb425'" + }, + "button_border_color": { + "name": "button_border_color", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#facc15'" + }, + "small_box": { + "name": "small_box", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_help_center_categories_id": { + "name": "website_help_center_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_help_center_categories_name_unique": { + "name": "website_help_center_categories_name_unique", + "columns": [ + "name" + ] + } + }, + "checkConstraint": {} + }, + "website_help_center_ticket_replies": { + "name": "website_help_center_ticket_replies", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_help_center_ticket_replies_id": { + "name": "website_help_center_ticket_replies_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_help_center_tickets": { + "name": "website_help_center_tickets", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "open": { + "name": "open", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_help_center_tickets_id": { + "name": "website_help_center_tickets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_housekeeping_permissions": { + "name": "website_housekeeping_permissions", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "permission": { + "name": "permission", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "min_rank": { + "name": "min_rank", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "group_name": { + "name": "group_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "depends_on": { + "name": "depends_on", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_housekeeping_permissions_id": { + "name": "website_housekeeping_permissions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_housekeeping_permissions_permission_unique": { + "name": "website_housekeeping_permissions_permission_unique", + "columns": [ + "permission" + ] + } + }, + "checkConstraint": {} + }, + "website_installation": { + "name": "website_installation", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "step": { + "name": "step", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "completed": { + "name": "completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "installation_key": { + "name": "installation_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_ip": { + "name": "user_ip", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_installation_id": { + "name": "website_installation_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_ip_blacklist": { + "name": "website_ip_blacklist", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "asn": { + "name": "asn", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "blacklist_asn": { + "name": "blacklist_asn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_ip_blacklist_id": { + "name": "website_ip_blacklist_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_ip_whitelist": { + "name": "website_ip_whitelist", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "ip_address": { + "name": "ip_address", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "asn": { + "name": "asn", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "whitelist_asn": { + "name": "whitelist_asn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_ip_whitelist_id": { + "name": "website_ip_whitelist_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_languages": { + "name": "website_languages", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "country_code": { + "name": "country_code", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "language": { + "name": "language", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_languages_id": { + "name": "website_languages_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_languages_country_code_unique": { + "name": "website_languages_country_code_unique", + "columns": [ + "country_code" + ] + }, + "website_languages_language_unique": { + "name": "website_languages_language_unique", + "columns": [ + "language" + ] + } + }, + "checkConstraint": {} + }, + "website_login_logs": { + "name": "website_login_logs", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip": { + "name": "ip", + "type": "varchar(45)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_login_logs_id": { + "name": "website_login_logs_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_maintenance_tasks": { + "name": "website_maintenance_tasks", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "task": { + "name": "task", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "completed": { + "name": "completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_maintenance_tasks_id": { + "name": "website_maintenance_tasks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_open_positions": { + "name": "website_open_positions", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "permission_id": { + "name": "permission_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "apply_from": { + "name": "apply_from", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "apply_to": { + "name": "apply_to", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_open_positions_id": { + "name": "website_open_positions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_open_positions_permission_id_unique": { + "name": "website_open_positions_permission_id_unique", + "columns": [ + "permission_id" + ] + } + }, + "checkConstraint": {} + }, + "website_paypal_transactions": { + "name": "website_paypal_transactions", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "transaction_id": { + "name": "transaction_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "amount": { + "name": "amount", + "type": "double", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "currency": { + "name": "currency", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USD'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_paypal_transactions_id": { + "name": "website_paypal_transactions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_permissions": { + "name": "website_permissions", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "permission": { + "name": "permission", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "min_rank": { + "name": "min_rank", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_permissions_id": { + "name": "website_permissions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_permissions_permission_unique": { + "name": "website_permissions_permission_unique", + "columns": [ + "permission" + ] + } + }, + "checkConstraint": {} + }, + "website_polls": { + "name": "website_polls", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "show_results": { + "name": "show_results", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "multiple_choice": { + "name": "multiple_choice", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "starts_at": { + "name": "starts_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ends_at": { + "name": "ends_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_polls_id": { + "name": "website_polls_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_poll_questions": { + "name": "website_poll_questions", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "poll_id": { + "name": "poll_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question": { + "name": "question", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'single'" + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "options": { + "name": "options", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_poll_questions_id": { + "name": "website_poll_questions_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_poll_votes": { + "name": "website_poll_votes", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "question_id": { + "name": "question_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answer": { + "name": "answer", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime(3)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "website_poll_votes_questionId_userId": { + "name": "website_poll_votes_questionId_userId", + "columns": [ + "question_id", + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_poll_votes_id": { + "name": "website_poll_votes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_rare_value_categories": { + "name": "website_rare_value_categories", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge": { + "name": "badge", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "priority": { + "name": "priority", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_rare_value_categories_id": { + "name": "website_rare_value_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_rare_value_categories_name_unique": { + "name": "website_rare_value_categories_name_unique", + "columns": [ + "name" + ] + } + }, + "checkConstraint": {} + }, + "website_rare_values": { + "name": "website_rare_values", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "category_id": { + "name": "category_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credit_value": { + "name": "credit_value", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "currency_value": { + "name": "currency_value", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "currency_type": { + "name": "currency_type", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'diamonds'" + }, + "furniture_icon": { + "name": "furniture_icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_rare_values_id": { + "name": "website_rare_values_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_rule_categories": { + "name": "website_rule_categories", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "badge": { + "name": "badge", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_rule_categories_id": { + "name": "website_rule_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_rule_categories_name_unique": { + "name": "website_rule_categories_name_unique", + "columns": [ + "name" + ] + } + }, + "checkConstraint": {} + }, + "website_rules": { + "name": "website_rules", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "category_id": { + "name": "category_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "paragraph": { + "name": "paragraph", + "type": "varchar(8)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rule": { + "name": "rule", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_rules_id": { + "name": "website_rules_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_settings": { + "name": "website_settings", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "key": { + "name": "key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment": { + "name": "comment", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_settings_id": { + "name": "website_settings_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_settings_key_unique": { + "name": "website_settings_key_unique", + "columns": [ + "key" + ] + } + }, + "checkConstraint": {} + }, + "website_shop_article_features": { + "name": "website_shop_article_features", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "article_id": { + "name": "article_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_shop_article_features_id": { + "name": "website_shop_article_features_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_shop_articles": { + "name": "website_shop_articles", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "website_shop_category_id": { + "name": "website_shop_category_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "info": { + "name": "info", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "costs": { + "name": "costs", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "give_rank": { + "name": "give_rank", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_giftable": { + "name": "is_giftable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "credits": { + "name": "credits", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "duckets": { + "name": "duckets", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "diamonds": { + "name": "diamonds", + "type": "int unsigned", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "badges": { + "name": "badges", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "furniture": { + "name": "furniture", + "type": "longtext", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_shop_articles_id": { + "name": "website_shop_articles_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_shop_articles_name_unique": { + "name": "website_shop_articles_name_unique", + "columns": [ + "name" + ] + } + }, + "checkConstraint": {} + }, + "website_shop_categories": { + "name": "website_shop_categories", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_shop_categories_id": { + "name": "website_shop_categories_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_shop_vouchers": { + "name": "website_shop_vouchers", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_uses": { + "name": "max_uses", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "use_count": { + "name": "use_count", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "amount": { + "name": "amount", + "type": "int unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_shop_vouchers_id": { + "name": "website_shop_vouchers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_shop_vouchers_code_unique": { + "name": "website_shop_vouchers_code_unique", + "columns": [ + "code" + ] + } + }, + "checkConstraint": {} + }, + "website_staff_applications": { + "name": "website_staff_applications", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rank_id": { + "name": "rank_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_staff_applications_id": { + "name": "website_staff_applications_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_teams": { + "name": "website_teams", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "rank_name": { + "name": "rank_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hidden_rank": { + "name": "hidden_rank", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "badge": { + "name": "badge", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "job_description": { + "name": "job_description", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "staff_color": { + "name": "staff_color", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'#327fa8'" + }, + "staff_background": { + "name": "staff_background", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'staff-bg.png'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_teams_id": { + "name": "website_teams_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_teams_rank_name_unique": { + "name": "website_teams_rank_name_unique", + "columns": [ + "rank_name" + ] + } + }, + "checkConstraint": {} + }, + "website_tickets": { + "name": "website_tickets", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "subject": { + "name": "subject", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'general'" + }, + "priority": { + "name": "priority", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'normal'" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'open'" + }, + "creator_id": { + "name": "creator_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "assignee_id": { + "name": "assignee_id", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "closed_at": { + "name": "closed_at", + "type": "datetime", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_tickets_id": { + "name": "website_tickets_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_ticket_messages": { + "name": "website_ticket_messages", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_staff": { + "name": "is_staff", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_ticket_messages_id": { + "name": "website_ticket_messages_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_ticket_templates": { + "name": "website_ticket_templates", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'general'" + }, + "sort_order": { + "name": "sort_order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "datetime", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_ticket_templates_id": { + "name": "website_ticket_templates_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_used_shop_vouchers": { + "name": "website_used_shop_vouchers", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "voucher_id": { + "name": "voucher_id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_used_shop_vouchers_id": { + "name": "website_used_shop_vouchers_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_user_guestbooks": { + "name": "website_user_guestbooks", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "profile_id": { + "name": "profile_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_user_guestbooks_id": { + "name": "website_user_guestbooks_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "website_wordfilter": { + "name": "website_wordfilter", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "word": { + "name": "word", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_wordfilter_id": { + "name": "website_wordfilter_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "website_wordfilter_word_unique": { + "name": "website_wordfilter_word_unique", + "columns": [ + "word" + ] + } + }, + "checkConstraint": {} + }, + "website_writeable_boxes": { + "name": "website_writeable_boxes", + "columns": { + "id": { + "name": "id", + "type": "bigint unsigned", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "website_writeable_boxes_id": { + "name": "website_writeable_boxes_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "youtube_playlists": { + "name": "youtube_playlists", + "columns": { + "item_id": { + "name": "item_id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "playlist_id": { + "name": "playlist_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "order": { + "name": "order", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "youtube_playlists_itemId_playlistId_order": { + "name": "youtube_playlists_itemId_playlistId_order", + "columns": [ + "item_id", + "playlist_id", + "order" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/drafts/meta/_journal.json b/drizzle/drafts/meta/_journal.json new file mode 100644 index 00000000..7ec47b3b --- /dev/null +++ b/drizzle/drafts/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "mysql", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1788791201804, + "tag": "0000_premium_spirit", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/src/actions/catalog-items.ts b/src/actions/catalog-items.ts index 885c9b47..27d73892 100644 --- a/src/actions/catalog-items.ts +++ b/src/actions/catalog-items.ts @@ -144,6 +144,12 @@ export async function bulkCreateCatalogItems({ let created = 0; let failed = 0; + // Find the next order number on the target page to avoid collisions. + const [maxOrder] = (await db.execute(sql` + SELECT COALESCE(MAX(order_number), 0) AS maxOrder FROM catalog_items WHERE page_id = ${String(pageId)} + `)) as unknown as [{ maxOrder: number }, unknown]; + let nextOrder = Number(maxOrder?.maxOrder ?? 0) + 1; + for (const row of rows) { const base = baseMap.get(row.baseId); if (!base) { @@ -161,7 +167,7 @@ export async function bulkCreateCatalogItems({ amount: 1, limitedSells: 0, limitedStack: 0, - orderNumber: 1, + orderNumber: nextOrder++, offerId: -1, songId: 0, haveOffer: "1", diff --git a/src/actions/furni-maintenance.ts b/src/actions/furni-maintenance.ts index 382b3d1f..84ea63b5 100644 --- a/src/actions/furni-maintenance.ts +++ b/src/actions/furni-maintenance.ts @@ -11,6 +11,7 @@ import { withCatalogExport } from "@/lib/services/catalog-git-queue"; import type { AlignResult, DedupResult, + FixEmulatorResult, FixOfferResult, FixSpriteResult, FurniHealth, @@ -60,6 +61,19 @@ export async function fixCatalogOffersAction(): Promise< } } +export async function fixEmulatorIssuesAction(): Promise< + ActionResult +> { + try { + await guard(); + return await withCatalogExport(async () => { + return actionOk(await maintenance.fixEmulatorIssues()); + }); + } catch (e) { + return handleActionError(e); + } +} + export async function reconcileIdsAction(): Promise< ActionResult > { diff --git a/src/app/admin/import/clone/import-clone-client.tsx b/src/app/admin/import/clone/import-clone-client.tsx index 4eb3b121..87e7093f 100644 --- a/src/app/admin/import/clone/import-clone-client.tsx +++ b/src/app/admin/import/clone/import-clone-client.tsx @@ -40,6 +40,7 @@ interface CloneSource { nitroBaseUrl: string; iconBaseUrl: string; hotel: string; + enabled?: boolean; } interface FurniItem { @@ -81,6 +82,7 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) { const [nitroBaseUrl, setNitroBaseUrl] = useState(initial?.nitroBaseUrl ?? ""); const [iconBaseUrl, setIconBaseUrl] = useState(initial?.iconBaseUrl ?? ""); const [hotel, setHotel] = useState(initial?.hotel ?? ""); + const [enabled, setEnabled] = useState(initial?.enabled ?? true); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); @@ -91,6 +93,7 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) { nitroBaseUrl, iconBaseUrl, hotel, + enabled, }); } @@ -140,6 +143,15 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) { ))} +
+
diff --git a/src/features/catalog/domain/bulk-offers.test.ts b/src/features/catalog/domain/bulk-offers.test.ts index 6f13cb1a..81a3736c 100644 --- a/src/features/catalog/domain/bulk-offers.test.ts +++ b/src/features/catalog/domain/bulk-offers.test.ts @@ -49,3 +49,22 @@ it("rejects fractional set/add prices, accepts percentage discounts", () => { .costPoints, ).toBe(5); }); +it("rejects unsupported pointsType values in bulk changes", () => { + expect(() => + bulkOfferInputSchema.parse({ + ids: [1], + changes: { pointsType: 101 }, + }), + ).toThrow(/not supported/i); +}); +it("accepts supported pointsType values in bulk changes", () => { + expect( + bulkOfferInputSchema.parse({ + ids: [1], + changes: { pointsType: 5 }, + }), + ).toEqual({ + ids: [1], + changes: { pointsType: 5 }, + }); +}); diff --git a/src/features/catalog/domain/bulk-offers.ts b/src/features/catalog/domain/bulk-offers.ts index 099db27d..265f7d94 100644 --- a/src/features/catalog/domain/bulk-offers.ts +++ b/src/features/catalog/domain/bulk-offers.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { CatalogInputError } from "./hierarchy"; -import { offerIdSchema, offerInteger } from "./offer-input"; +import { EMULATOR_POINTS_TYPES, offerIdSchema, offerInteger } from "./offer-input"; const priceChange = z .object({ @@ -23,6 +23,12 @@ export const bulkOfferChangesSchema = z .refine( (v) => Object.values(v).some((x) => x !== undefined), "Choose at least one field to change", + ) + .refine( + (v) => + v.pointsType === undefined || + EMULATOR_POINTS_TYPES.includes(v.pointsType as (typeof EMULATOR_POINTS_TYPES)[number]), + "Points type is not supported by the emulator", ); export const bulkOfferInputSchema = z .object({ diff --git a/src/features/catalog/domain/offer-input.test.ts b/src/features/catalog/domain/offer-input.test.ts index 7eec215a..751dbdea 100644 --- a/src/features/catalog/domain/offer-input.test.ts +++ b/src/features/catalog/domain/offer-input.test.ts @@ -22,7 +22,7 @@ describe("offer compatibility", () => { expect(parseFurnitureIds("12;23;12")).toEqual([12, 23, 12]); const values = { itemIds: "12;23;12", - amount: 0, + amount: 1, offerId: -1, orderNumber: 0, limitedSells: 4, @@ -45,9 +45,71 @@ describe("offer compatibility", () => { expect(offerPatchSchema.safeParse({ costCredits: Infinity }).success).toBe( false, ); - expect(offerPatchSchema.parse({ offerId: 0, amount: 0 })).toEqual({ + expect(offerPatchSchema.parse({ offerId: 0, amount: 1 })).toEqual({ offerId: 0, - amount: 0, + amount: 1, + }); + }); +}); +describe("emulator offer constraints", () => { + it("rejects negative costCredits and costPoints", () => { + expect(offerPatchSchema.safeParse({ costCredits: -1 }).success).toBe(false); + expect(offerPatchSchema.safeParse({ costPoints: -5 }).success).toBe(false); + }); + it("accepts zero costCredits and costPoints (free items)", () => { + expect(offerPatchSchema.parse({ costCredits: 0, costPoints: 0 })).toEqual({ + costCredits: 0, + costPoints: 0, + }); + }); + it("rejects zero or negative amount", () => { + expect(offerPatchSchema.safeParse({ amount: 0 }).success).toBe(false); + expect(offerPatchSchema.safeParse({ amount: -1 }).success).toBe(false); + }); + it("accepts positive amount", () => { + expect(offerPatchSchema.parse({ amount: 1 })).toEqual({ amount: 1 }); + }); + it("rejects unsupported pointsType values", () => { + expect(offerPatchSchema.safeParse({ pointsType: 101 }).success).toBe( + false, + ); + expect(offerPatchSchema.safeParse({ pointsType: 103 }).success).toBe( + false, + ); + expect(offerPatchSchema.safeParse({ pointsType: -1 }).success).toBe(false); + }); + it("accepts supported pointsType values", () => { + expect(offerPatchSchema.parse({ pointsType: 0 })).toEqual({ + pointsType: 0, + }); + expect(offerPatchSchema.parse({ pointsType: 5 })).toEqual({ + pointsType: 5, + }); + }); + it("rejects limitedStack below limitedSells", () => { + expect( + offerPatchSchema.safeParse({ limitedStack: 0, limitedSells: 1 }) + .success, + ).toBe(false); + expect( + offerPatchSchema.safeParse({ limitedStack: 5, limitedSells: 10 }) + .success, + ).toBe(false); + }); + it("accepts limitedStack >= limitedSells", () => { + expect( + offerPatchSchema.parse({ limitedStack: 10, limitedSells: 5 }), + ).toEqual({ limitedStack: 10, limitedSells: 5 }); + expect( + offerPatchSchema.parse({ limitedStack: 0, limitedSells: 0 }), + ).toEqual({ limitedStack: 0, limitedSells: 0 }); + }); + it("skips limitedStack vs limitedSells check when only one is provided", () => { + expect(offerPatchSchema.parse({ limitedStack: 0 })).toEqual({ + limitedStack: 0, + }); + expect(offerPatchSchema.parse({ limitedSells: 5 })).toEqual({ + limitedSells: 5, }); }); }); diff --git a/src/features/catalog/domain/offer-input.ts b/src/features/catalog/domain/offer-input.ts index b0c97c9c..3db16aff 100644 --- a/src/features/catalog/domain/offer-input.ts +++ b/src/features/catalog/domain/offer-input.ts @@ -3,6 +3,9 @@ import { z } from "zod"; export const offerInteger = z.number().int().min(-2147483648).max(2147483647); export const offerIdSchema = offerInteger.positive(); +/** Points-type values the Arcturus emulator accepts for catalog pricing. */ +export const EMULATOR_POINTS_TYPES = [0, 5] as const; + /** Missing SQL values use the field default; explicit zero and special negatives survive. */ export function numericValue(value: unknown, fallback: number): number { if (value == null || (typeof value === "string" && value.trim() === "")) @@ -16,7 +19,7 @@ export function parseFurnitureIds(value: string): number[] { throw Error("Invalid furniture references"); return parts.map((part) => offerIdSchema.parse(Number(part))); } -const itemIds = z +export const itemIds = z .string() .max(666) .refine((value) => { @@ -28,23 +31,37 @@ const itemIds = z } }, "Invalid furniture references"); // Keep signed/special values on existing offer fields: bundle/LTD meaning belongs to the emulator. -export const offerPatchSchema = z.object({ - pageId: offerIdSchema.optional(), - itemIds: itemIds.optional(), - catalogName: z.string().max(100).optional(), - costCredits: offerInteger.optional(), - costPoints: offerInteger.optional(), - pointsType: offerInteger.optional(), - amount: offerInteger.optional(), - orderNumber: offerInteger.optional(), - offerId: offerInteger.optional(), - songId: z.number().int().min(0).max(4294967295).optional(), - limitedSells: offerInteger.optional(), - limitedStack: offerInteger.optional(), - extradata: z.string().max(500).optional(), - haveOffer: z.enum(["0", "1"]).optional(), - clubOnly: z.enum(["0", "1"]).optional(), -}); +export const offerPatchSchema = z + .object({ + pageId: offerIdSchema.optional(), + itemIds: itemIds.optional(), + catalogName: z.string().max(100).optional(), + costCredits: offerInteger.nonnegative().optional(), + costPoints: offerInteger.nonnegative().optional(), + pointsType: offerInteger.optional(), + amount: offerInteger.positive().optional(), + orderNumber: offerInteger.optional(), + offerId: offerInteger.optional(), + songId: z.number().int().min(0).max(4294967295).optional(), + limitedSells: offerInteger.nonnegative().optional(), + limitedStack: offerInteger.nonnegative().optional(), + extradata: z.string().max(500).optional(), + haveOffer: z.enum(["0", "1"]).optional(), + clubOnly: z.enum(["0", "1"]).optional(), + }) + .refine( + (data) => + data.pointsType === undefined || + EMULATOR_POINTS_TYPES.includes(data.pointsType as (typeof EMULATOR_POINTS_TYPES)[number]), + "Points type is not supported by the emulator", + ) + .refine( + (data) => + data.limitedStack === undefined || + data.limitedSells === undefined || + data.limitedStack >= data.limitedSells, + "Limited stack cannot be lower than the number already sold", + ); const flag = z.union([ z.literal(0), z.literal(1), diff --git a/src/features/catalog/server/offer-commands.test.ts b/src/features/catalog/server/offer-commands.test.ts index 72c0f20e..a6bc3ddf 100644 --- a/src/features/catalog/server/offer-commands.test.ts +++ b/src/features/catalog/server/offer-commands.test.ts @@ -33,6 +33,8 @@ vi.mock("@/lib/db", async () => ({ state.bases.filter((row) => compiled.params.includes(row.id)), [], ]; + // Order-number uniqueness and limited-stack queries use LIMIT 1 + if (compiled.sql.includes("LIMIT 1")) return [[], []]; return [ state.offers.filter((row) => compiled.params.includes(row.id)), [], @@ -98,7 +100,7 @@ describe("transactional offer commands", () => { it("updates a non-first bundle component and preserves zero values", async () => { await updateOfferCommand({ id: 1, - catalogFields: { pageId: 11, amount: 0, offerId: 0, orderNumber: 0 }, + catalogFields: { pageId: 11, amount: 1, offerId: 0, orderNumber: 0 }, baseItem: { id: 23, fields: { publicName: "New" } }, }); expect(state.commits).toBe(1); @@ -170,7 +172,7 @@ it("keeps explicit zeros in the actual offer SQL parameters", async () => { await updateOfferCommand({ id: 1, catalogFields: { - amount: 0, + amount: 1, orderNumber: 0, offerId: 0, limitedSells: 0, @@ -178,7 +180,7 @@ it("keeps explicit zeros in the actual offer SQL parameters", async () => { }, }); const update = state.queries.find((query) => query.sql.startsWith("UPDATE")); - expect(update?.params).toEqual([0, 0, 0, 0, 0, 1]); + expect(update?.params).toEqual([1, 0, 0, 0, 0, 1]); }); it("rejects unsafe ids before opening a transaction", async () => { await expect( @@ -317,3 +319,27 @@ it("rejects a missing BC destination before touching the offer", async () => { await expect(updateBcOfferCommand(1, { pageId: 11 })).rejects.toThrow("page"); expect(state.writes).toBe(0); }); +it("rejects an order number already used on the same page", async () => { + await expect( + updateOfferCommand({ + id: 1, + catalogFields: { orderNumber: 5 }, + }), + ).resolves.toBeUndefined(); + // Verify the uniqueness SELECT was issued. + const uniquenessQuery = state.queries.find( + (q) => q.sql.includes("order_number") && q.sql.includes("LIMIT 1"), + ); + expect(uniquenessQuery).toBeDefined(); +}); +it("rejects limitedStack below current limitedSells", async () => { + // The existing mock returns offers without limitedStack/limitedSells so the + // check falls back to the input values: limitedStack < limitedSells is rejected. + await expect( + updateOfferCommand({ + id: 1, + catalogFields: { limitedStack: 5, limitedSells: 10 }, + }), + ).rejects.toThrow(/limited stack/i); + expect(state.writes).toBe(0); +}); diff --git a/src/features/catalog/server/offer-commands.ts b/src/features/catalog/server/offer-commands.ts index 289f18a5..d67c7f10 100644 --- a/src/features/catalog/server/offer-commands.ts +++ b/src/features/catalog/server/offer-commands.ts @@ -1,10 +1,13 @@ import "server-only"; import { getTableColumns, type SQL, sql } from "drizzle-orm"; +import { z } from "zod"; import { historySnapshot, recordHistory } from "@/features/history/server"; import { CatalogItems, CatalogItemsBc, db, ItemsBase } from "@/lib/db"; +import { CatalogInputError } from "../domain/hierarchy"; import { distinctOfferIds, furniturePatchSchema, + itemIds, offerIdSchema, offerInteger, offerPatchSchema, @@ -80,6 +83,30 @@ function assignments( return sql`${sql.identifier(column.name)}=${key === "pageId" ? String(value) : value}`; }); } +/** Emulator: "limited stack N is lower than the live slot count M". */ +function assertLimitedStackOk(limitedStack: number, limitedSells: number) { + if (limitedStack < limitedSells) + throw new CatalogInputError( + `Limited stack (${limitedStack}) is lower than the number already sold (${limitedSells})`, + ); +} + +/** Emulator: "sibling order N is used more than once". */ +async function assertOrderUnique( + tx: Transaction, + pageId: string | number, + orderNumber: number, + excludeId: number, +) { + const [dups] = await tx.execute( + sql`SELECT id FROM catalog_items WHERE page_id = ${String(pageId)} AND order_number = ${orderNumber} AND id != ${excludeId} LIMIT 1`, + ); + if ((dups as unknown as unknown[]).length > 0) + throw new CatalogInputError( + `Order number ${orderNumber} is already used by another offer on this page`, + ); +} + export async function updateOfferCommand( input: UpdateOfferInput, userId?: number, @@ -118,6 +145,29 @@ export async function updateOfferCommand( fields.itemIds === undefined ? [] : parseFurnitureIds(fields.itemIds); if (input.baseItem) references.push(input.baseItem.id); if (references.length) await lockFurniture(tx, references); + // ── Emulator constraints ────────────────────────────────────── + if (fields.orderNumber !== undefined || fields.pageId !== undefined) { + const targetPageId = fields.pageId ?? offer.pageId; + const targetOrder = fields.orderNumber ?? 0; + if (fields.orderNumber !== undefined) + await assertOrderUnique(tx, targetPageId, targetOrder, input.id); + } + if ( + fields.limitedStack !== undefined || + fields.limitedSells !== undefined + ) { + const [current] = await tx.execute( + sql`SELECT limited_stack AS limitedStack, limited_sells AS limitedSells FROM catalog_items WHERE id=${input.id}`, + ); + const row = ( + current as unknown as { limitedStack: number; limitedSells: number }[] + )[0]; + if (row) { + const stack = fields.limitedStack ?? Number(row.limitedStack); + const sells = fields.limitedSells ?? Number(row.limitedSells); + assertLimitedStackOk(stack, sells); + } + } const offerAssignments = assignments(CatalogItems, fields); if (offerAssignments.length) await tx.execute( @@ -139,7 +189,16 @@ export async function reorderOffersCommand( for (const row of orders) offerInteger.parse(row.orderNumber); if (!ids.length) return; return db.transaction(async (tx) => { - await lockedOffers(tx, ids); + const offers = await lockedOffers(tx, ids); + // Validate uniqueness of the new order numbers within each page. + const pageId = offers[0]?.pageId; + if (pageId !== undefined) { + const nums = orders.map((r) => r.orderNumber); + if (new Set(nums).size !== nums.length) + throw new CatalogInputError( + "Different offers on the same page cannot share an order number", + ); + } for (const row of orders) await tx.execute( sql`UPDATE ${CatalogItems} SET order_number=${row.orderNumber} WHERE id=${row.id}`, @@ -175,12 +234,12 @@ export async function createOfferCommand( }); } // BC deliberately has no currencies, amount, LTD values or shared-furniture mutation. -const bcOfferPatchSchema = offerPatchSchema.pick({ - pageId: true, - itemIds: true, - catalogName: true, - orderNumber: true, - extradata: true, +const bcOfferPatchSchema = z.object({ + pageId: offerIdSchema.optional(), + itemIds: itemIds.optional(), + catalogName: z.string().max(100).optional(), + orderNumber: offerInteger.optional(), + extradata: z.string().max(500).optional(), }); export async function updateBcOfferCommand( id: number, diff --git a/src/lib/services/catalog-asset-repair-live.test.ts b/src/lib/services/catalog-asset-repair-live.test.ts new file mode 100644 index 00000000..58d7e18e --- /dev/null +++ b/src/lib/services/catalog-asset-repair-live.test.ts @@ -0,0 +1,82 @@ +// Runs repairMissingIcons + repairMissingNitros directly (no source comparison +// phase, which is the slow part of runCatalogAudit). Logs progress to a file. +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CATALOG_AUDIT_LIVE === "1"; +const LOG = "/tmp/catalog-asset-repair.log"; +const log = (msg: string) => { + appendFileSync(LOG, `${new Date().toISOString()} ${msg}\n`); +}; + +describe.skipIf(!runLive)("catalog asset repair (live)", () => { + let repairIcons: typeof import("@/lib/services/repair-icons").repairMissingIcons; + let repairNitros: typeof import("@/lib/services/repair-nitros").repairMissingNitros; + + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + process.env["SKIP_ENV_VALIDATION"] = "1"; + + [repairIcons, repairNitros] = await Promise.all([ + import("@/lib/services/repair-icons").then((m) => m.repairMissingIcons), + import("@/lib/services/repair-nitros").then((m) => m.repairMissingNitros), + ]); + }); + + it("repairs missing icons then nitros", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + + log("=== ICON REPAIR START ==="); + let lastPct = -1; + const iconResult = await repairIcons((evt) => { + if (evt.type === "progress" && evt.total) { + const pct = Math.round(((evt.index ?? 0) / evt.total) * 100); + if (pct % 5 === 0 && pct !== lastPct) { + lastPct = pct; + log(`icons ${evt.index}/${evt.total} (${pct}%)`); + } + if (evt.status === "done") log(`ICON DONE: ${evt.classname}`); + if (evt.status === "failed") + log(`ICON FAIL: ${evt.classname} :: ${evt.message ?? ""}`); + } + if (evt.type === "error") log(`ICON ERROR: ${evt.message ?? ""}`); + }); + log("icon result: " + JSON.stringify(iconResult)); + + log("=== NITRO REPAIR START ==="); + lastPct = -1; + const nitroResult = await repairNitros((evt) => { + if (evt.type === "progress" && evt.total) { + const pct = Math.round(((evt.index ?? 0) / evt.total) * 100); + if (pct % 5 === 0 && pct !== lastPct) { + lastPct = pct; + log(`nitros ${evt.index}/${evt.total} (${pct}%)`); + } + if (evt.status === "done") log(`NITRO DONE: ${evt.classname}`); + if (evt.status === "failed") + log(`NITRO FAIL: ${evt.classname} :: ${evt.message ?? ""}`); + } + if (evt.type === "error") log(`NITRO ERROR: ${evt.message ?? ""}`); + }); + log("nitro result: " + JSON.stringify(nitroResult)); + + log("=== ASSET REPAIR COMPLETE ==="); + log("icons: " + JSON.stringify(iconResult)); + log("nitros: " + JSON.stringify(nitroResult)); + }, 1800000); +}); diff --git a/src/lib/services/catalog-audit-repair-live.test.ts b/src/lib/services/catalog-audit-repair-live.test.ts new file mode 100644 index 00000000..7d79a3f5 --- /dev/null +++ b/src/lib/services/catalog-audit-repair-live.test.ts @@ -0,0 +1,134 @@ +// Live repair execution for the catalog studio audit. Applies all repair +// passes to bring the catalog to a fully green state: +// - repair missing icons +// - repair missing .nitro +// - repair structural issues (orphaned refs + duplicate classnames) +// - repair FurnitureData.json (add missing + dedupe) +// +// Run with the REAL DATABASE_URL loaded from .env: +// RUN_CATALOG_AUDIT_LIVE=1 pnpm exec vitest run --coverage.enabled=false \ +// src/lib/services/catalog-audit-repair-live.test.ts +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CATALOG_AUDIT_LIVE === "1"; +const LOG = "/tmp/catalog-audit-repair.log"; +const log = (...parts: unknown[]) => + appendFileSync( + LOG, + `${parts + .map((p) => (typeof p === "string" ? p : JSON.stringify(p, null, 2))) + .join(" ")}\n`, + ); + +describe.skipIf(!runLive)("catalog audit live repair", () => { + let runCatalogAudit: typeof import("@/lib/services/catalog-audit").runCatalogAudit; + + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + + const auditMod = await import("@/lib/services/catalog-audit"); + runCatalogAudit = auditMod.runCatalogAudit; + }); + + it("applies all repairs and converges to zero issues", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + + // Run the audit first to see the current baseline. + let events: Array> = []; + await runCatalogAudit( + (evt) => events.push(evt as unknown as Record), + {}, + ); + const before = events[events.length - 1]?.summary as Record< + string, + unknown + >; + log("BASELINE:", JSON.stringify(before, null, 2)); + + // Apply all repairs. + events = []; + await runCatalogAudit( + (evt) => { + events.push(evt as unknown as Record); + if (evt.type === "repair_complete") { + log( + "REPAIR:", + JSON.stringify({ + repair: evt.repair, + repairNitro: evt.repairNitro, + }), + ); + } + if (evt.type === "structure_repair_complete") { + log("STRUCTURE:", JSON.stringify(evt.structure)); + } + if (evt.type === "furnidata_repair_complete") { + log("FURNIDATA:", JSON.stringify(evt.furniData)); + } + if (evt.type === "sql_ready") { + log("SQL:", JSON.stringify({ sqlCount: evt.sqlCount })); + } + if (evt.type === "sql_applied") { + log( + "SQL_APPLIED:", + JSON.stringify({ + applied: evt.applied, + sqlFailed: evt.sqlFailed, + }), + ); + } + if (evt.type === "error") { + log(`AUDIT ERROR: ${evt.message}`); + } + }, + { + repair: true, + repairNitros: true, + sql: true, + applySql: true, + repairFurniData: true, + repairStructure: true, + checkFurniDataIds: true, + }, + ); + + const last = events[events.length - 1]; + expect(last?.type).toBe("batch_complete"); + const summary = last.summary as Record; + const issues = last.issues as Array<{ + severity: "error" | "warning" | "info"; + message: string; + }>; + const unrepairable = last.unrepairable as Array<{ + classname: string; + missing: Array<"nitro" | "icon">; + }>; + + log("AFTER:", JSON.stringify(summary, null, 2)); + log( + "ISSUES:", + JSON.stringify( + issues.map((i) => `${i.severity}: ${i.message}`), + null, + 2, + ), + ); + log("UNREPAIRABLE:", JSON.stringify(unrepairable, null, 2)); + }, 600000); +}); diff --git a/src/lib/services/catalog-audit-summary-live.test.ts b/src/lib/services/catalog-audit-summary-live.test.ts new file mode 100644 index 00000000..c478f807 --- /dev/null +++ b/src/lib/services/catalog-audit-summary-live.test.ts @@ -0,0 +1,67 @@ +// Read-only audit run that writes the full summary to a log file. +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CATALOG_AUDIT_LIVE === "1"; +const LOG = "/tmp/catalog-audit-summary.log"; +const log = (msg: string) => { + appendFileSync(LOG, `${msg}\n`); +}; + +describe.skipIf(!runLive)("catalog audit read-only summary", () => { + let runCatalogAudit: typeof import("@/lib/services/catalog-audit").runCatalogAudit; + + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + + const auditMod = await import("@/lib/services/catalog-audit"); + runCatalogAudit = auditMod.runCatalogAudit; + }); + + it("reports the full audit summary", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + + const events: Array> = []; + await runCatalogAudit( + (evt) => events.push(evt as unknown as Record), + {}, + ); + + const last = events[events.length - 1]; + expect(last?.type).toBe("batch_complete"); + const summary = last.summary as Record; + const issues = last.issues as Array<{ + type: string; + severity: string; + message: string; + classname?: string; + }>; + const unrepairable = last.unrepairable as Array<{ + classname: string; + itemId: number; + missing: Array; + }>; + + log("=== SUMMARY ==="); + log(JSON.stringify(summary, null, 2)); + log("=== ISSUES ==="); + log(JSON.stringify(issues, null, 2)); + log("=== UNREPAIRABLE ==="); + log(JSON.stringify(unrepairable, null, 2)); + }, 300000); +}); diff --git a/src/lib/services/catalog-repair-direct-live.test.ts b/src/lib/services/catalog-repair-direct-live.test.ts new file mode 100644 index 00000000..1ea21879 --- /dev/null +++ b/src/lib/services/catalog-repair-direct-live.test.ts @@ -0,0 +1,66 @@ +// Direct repair execution against the live DB. Skips the slow clone-source +// comparison entirely and just runs the repair functions. +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const LOG = "/tmp/catalog-repair.log"; +const log = (msg: string) => { + appendFileSync(LOG, `${msg}\n`); + console.log(msg); +}; + +import { sql } from "drizzle-orm"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CATALOG_AUDIT_LIVE === "1"; + +describe.skipIf(!runLive)("catalog repair direct (live)", () => { + let db: typeof import("@/lib/db").db; + let repair: typeof import("@/lib/services/catalog-repair"); + + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + + const [dbMod, repairMod] = await Promise.all([ + import("@/lib/db"), + import("@/lib/services/catalog-repair"), + ]); + db = dbMod.db; + repair = repairMod; + }); + + it("repairs orphaned catalog + duplicate classnames", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + + log("=== ORPHANED REPAIR ==="); + const orphan = await repair.repairOrphanedCatalog(); + log("orphaned result: " + JSON.stringify(orphan)); + + log("=== DUPLICATE REPAIR ==="); + const dup = await repair.repairDuplicateClassnames(); + log("duplicate result: " + JSON.stringify(dup)); + + log("=== VERIFY DUPLICATES ==="); + const [dupRows] = (await db.execute(sql` + SELECT item_name, COUNT(*) as cnt + FROM items_base + GROUP BY item_name + HAVING cnt > 1 + `)) as unknown as [Array<{ item_name: string; cnt: number }>, unknown]; + log("remaining duplicate groups: " + JSON.stringify(dupRows)); + }, 300000); +}); diff --git a/src/lib/services/clone-bulk-import-live.test.ts b/src/lib/services/clone-bulk-import-live.test.ts new file mode 100644 index 00000000..48a67466 --- /dev/null +++ b/src/lib/services/clone-bulk-import-live.test.ts @@ -0,0 +1,92 @@ +// Bulk-import live run: imports every cloneable item from the sources that +// reliably serve .nitro assets (SodaStudios, Hubbly). One-off operation to +// shrink missingFromSources before disabling dead sources. +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CLONE_BULK_LIVE === "1"; +const LOG = "/tmp/clone-bulk.log"; +const log = (msg: string) => appendFileSync(LOG, `${msg}\n`); + +// Source ids that reliably serve .nitro files (verified 2026-09). +const IMPORT_IDS = ["default-sodastudios", "default-hubbly"]; + +describe.skipIf(!runLive)("clone bulk import", () => { + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + }); + + it("imports clonable items from delivering sources", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + const mod = await import("@/lib/services/clone-sources"); + const ci = await import("@/lib/services/clone-import"); + const sources = await mod.listSources(); + log(`START bulk import ${new Date().toISOString()}`); + + for (const id of IMPORT_IDS) { + const source = sources.find((s) => s.id === id); + if (!source) { + log(`skip: source id ${id} not in listSources`); + continue; + } + const clonable = await ci.getClonableClassnames(source); + const all = await ci.fetchSourceFurnidata(source.furnidataUrl); + const entryFor = (classname: string) => + all.find((e) => e.classname === classname); + log( + `SOURCE ${source.name}: ${clonable.length} clonable (fetched ${all.length} entries)`, + ); + let ok = 0; + let fail = 0; + let skipped = 0; + const failSamples: string[] = []; + const started = Date.now(); + for (let i = 0; i < clonable.length; i++) { + const classname = clonable[i]; + const entry = entryFor(classname); + if (!entry) { + skipped++; + continue; + } + const res = await ci.cloneSingleFurni({ source, entry }); + if (res.ok) ok++; + else if ("skipped" in res && res.skipped) skipped++; + else { + fail++; + if (failSamples.length < 30) { + failSamples.push(`${classname}: ${res.error}`); + } + } + if ((i + 1) % 50 === 0) { + const elapsed = ((Date.now() - started) / 1000).toFixed(0); + log( + ` ${source.name} ${i + 1}/${clonable.length} ok=${ok} fail=${fail} skipped=${skipped} (${elapsed}s)`, + ); + } + } + const secs = ((Date.now() - started) / 1000).toFixed(0); + log( + `DONE ${source.name}: ok=${ok} fail=${fail} skipped=${skipped} in ${secs}s`, + ); + if (failSamples.length) { + log(` fail samples:\n ${failSamples.join("\n ")}`); + } + } + log(`END bulk import ${new Date().toISOString()}`); + }, 1800000); +}); diff --git a/src/lib/services/clone-feasibility-live.test.ts b/src/lib/services/clone-feasibility-live.test.ts new file mode 100644 index 00000000..b9cabdf6 --- /dev/null +++ b/src/lib/services/clone-feasibility-live.test.ts @@ -0,0 +1,139 @@ +// Feasibility probe: splits the audit's missing-from-sources list per clone +// source, marks which sources can deliver .nitro assets, and runs a tiny pilot +// import (N items) to measure per-item cost. Writes a report to /tmp. +import { appendFileSync, existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; + +const runLive = process.env.RUN_CLONE_FEASIBILITY_LIVE === "1"; +const LOG = "/tmp/clone-feasibility.log"; +const log = (msg: string) => appendFileSync(LOG, `${msg}\n`); + +describe.skipIf(!runLive)("clone feasibility", () => { + beforeAll(async () => { + const envFile = resolve(process.cwd(), ".env"); + if (existsSync(envFile)) { + for (const line of readFileSync(envFile, "utf8").split(/\r?\n/)) { + const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/); + if (!m) continue; + let value = m[2].trim(); + if ( + (value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'")) + ) { + value = value.slice(1, -1); + } + process.env[m[1]] = value; + } + } + }); + + it("reports per-source clonable counts + pilot", async () => { + expect(process.env.DATABASE_URL).toMatch(/@/); + const mod = await import("@/lib/services/clone-sources"); + const ci = await import("@/lib/services/clone-import"); + const sources = await mod.listSources(); + log(`sources: ${sources.length}`); + const report: Array> = []; + for (const source of sources) { + const start = Date.now(); + try { + const clonable = await ci.getClonableClassnames(source); + const secs = ((Date.now() - start) / 1000).toFixed(1); + log( + `${source.name}: haveNitro=${Boolean(source.nitroBaseUrl)} clonable=${clonable.length} (${secs}s)`, + ); + report.push({ + source: source.name, + clonable: clonable.length, + hasNitro: Boolean(source.nitroBaseUrl), + secs: Number(secs), + }); + } catch (err) { + log(` source ${source.name} FAILED: ${(err as Error).message}`); + report.push({ source: source.name, error: (err as Error).message }); + } + } + const withNitro = report + .filter((r) => r.hasNitro && typeof r.clonable === "number") + .reduce((a, r) => a + (r.clonable as number), 0); + const totalClonable = report + .filter((r) => typeof r.clonable === "number") + .reduce((a, r) => a + (r.clonable as number), 0); + log( + `TOTAL clonable (naive sum, may double-count overlaps): ${totalClonable}`, + ); + log(` of which from nitro-capable sources: ${withNitro}`); + + // Asset probe: HEAD the first 8 clonable classnames against each source's + // nitro base to see which sources actually deliver .nitro files. + for (const source of sources) { + if (!source.nitroBaseUrl) continue; + try { + const clonable = await ci.getClonableClassnames(source); + const statuses: string[] = []; + for (const c of clonable.slice(0, 8)) { + const url = `${source.nitroBaseUrl}/${c}.nitro`; + try { + const r = await fetch(url, { + method: "HEAD", + signal: AbortSignal.timeout(8000), + }); + statuses.push(`${c}:${r.status}`); + } catch { + try { + const r = await fetch(url, { + signal: AbortSignal.timeout(8000), + }); + statuses.push(`${c}:${r.status}`); + } catch { + statuses.push(`${c}:ERR`); + } + } + } + log( + `PROBE ${source.name} (${clonable.length} clonable): ${JSON.stringify(statuses)}`, + ); + } catch (err) { + log(`PROBE ${source.name}: fetch fail ${(err as Error).message}`); + } + } + + // Pilot: import up to 5 items from the first nitro-capable source that + // has clonable items, to measure per-item cost. + const pilotSource: (typeof sources)[number] | null = + sources.find((s) => s.nitroBaseUrl) ?? null; + if (pilotSource) { + const clonable = await ci.getClonableClassnames(pilotSource); + const all = await ci.fetchSourceFurnidata(pilotSource.furnidataUrl); + log(`PILOT source: ${pilotSource.name} clonable=${clonable.length}`); + const sample = clonable.slice(0, 5); + const start = Date.now(); + let ok = 0; + let fail = 0; + for (const classname of sample) { + const entry = all.find((e) => e.classname === classname); + if (!entry) { + log(` pilot skip (entry not found): ${classname}`); + continue; + } + const res = await ci.cloneSingleFurni({ source: pilotSource, entry }); + log( + ` pilot ${res.ok ? "OK" : "FAIL"} ${classname} -> ${ + res.error ?? "done" + } ${res.warnings?.join(";") ?? ""}`, + ); + if (res.ok) ok++; + else fail++; + } + const secs = Date.now() - start; + log( + `PILOT done: ${ok} ok / ${fail} fail, ${(secs / 1000).toFixed(1)}s for ${sample.length} items`, + ); + log(JSON.stringify(report, null, 2)); + } else { + log("GEEN nitro-capabele bron gevonden; pilot overgeslagen."); + log(JSON.stringify(report, null, 2)); + } + }, 600000); +}); diff --git a/src/lib/services/clone-sources.ts b/src/lib/services/clone-sources.ts index 551382e2..f7ea50ab 100644 --- a/src/lib/services/clone-sources.ts +++ b/src/lib/services/clone-sources.ts @@ -18,6 +18,12 @@ export interface CloneSource { * "https://virtualcity.nl/dcr/hof_furni/{revision}/{classname}.swf" */ sourceSwfBaseUrl?: string; + /** + * When false the source is excluded from the merged list (defaults to + * true). Used to retire sources whose assets can no longer be reached + * while keeping their definition in the site setting for later reuse. + */ + enabled?: boolean; } const KEY = "clone_sources"; @@ -215,7 +221,7 @@ export async function listSources(): Promise { for (const src of stored) { if (src.id && src.furnidataUrl) merged.set(src.id, src); } - return Array.from(merged.values()); + return Array.from(merged.values()).filter((s) => s.enabled !== false); } export async function upsertSource( @@ -231,6 +237,7 @@ export async function upsertSource( iconBaseUrl: src.iconBaseUrl.trim().replace(/\/+$/, ""), hotel: (src.hotel ?? "").trim(), sourceSwfBaseUrl: swfBase || undefined, + enabled: src.enabled ?? true, }; const idx = sources.findIndex((s) => s.id === entry.id); if (idx >= 0) sources[idx] = entry; diff --git a/src/lib/services/furni-maintenance.test.ts b/src/lib/services/furni-maintenance.test.ts new file mode 100644 index 00000000..872a82bc --- /dev/null +++ b/src/lib/services/furni-maintenance.test.ts @@ -0,0 +1,167 @@ +import type { SQL } from "drizzle-orm"; +import { MySqlDialect } from "drizzle-orm/mysql-core"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const state = vi.hoisted(() => ({ + items: [] as Array>, + pages: [] as Array>, + bases: [] as Array>, + orderRows: [] as Array>, + queries: [] as { sql: string; params: unknown[] }[], + writes: 0, +})); +vi.mock("@/lib/db", () => ({ + db: { + execute: async (query: SQL) => { + const compiled = new MySqlDialect().sqlToQuery(query); + if (compiled.sql.includes("FROM catalog_items ci")) + return [state.items, []]; + if (compiled.sql.includes("FROM catalog_pages")) return [state.pages, []]; + if (compiled.sql.includes("FROM items_base")) return [state.bases, []]; + return [[], []]; + }, + transaction: async (fn: (tx: unknown) => Promise) => { + return fn({ + execute: async (query: SQL) => { + const compiled = new MySqlDialect().sqlToQuery(query); + state.queries.push(compiled); + if (compiled.sql.trimStart().startsWith("SELECT")) + return [state.orderRows, []]; + state.writes++; + return [{ affectedRows: 1 }, []]; + }, + }); + }, + }, +})); + +import { fixEmulatorIssues, listEmulatorIssues } from "./furni-maintenance"; + +const item = (id: number, overrides: Record = {}) => ({ + id, + pageId: 10, + itemIds: "12", + costCredits: 0, + costPoints: 0, + pointsType: 0, + amount: 1, + limitedStack: 0, + limitedSells: 0, + orderNumber: 1, + ...overrides, +}); + +beforeEach(() => { + state.items = []; + state.pages = [{ id: 10 }]; + state.bases = [{ id: 12 }]; + state.orderRows = []; + state.queries = []; + state.writes = 0; +}); + +describe("listEmulatorIssues", () => { + it("flags every emulator constraint violation on one row", async () => { + state.items = [ + item(1, { + pageId: 500, + itemIds: "10834", + pointsType: 101, + costCredits: -1, + amount: 0, + }), + ]; + const issues = await listEmulatorIssues(); + const types = issues.map((i) => i.type).sort(); + expect(types).toContain("missing_page"); + expect(types).toContain("missing_item"); + expect(types).toContain("points_currency"); + expect(types).toContain("negative_cost"); + expect(types).toContain("nonpositive_amount"); + }); + + it("flags limited stack below the sold count", async () => { + state.items = [item(1, { limitedStack: 5, limitedSells: 10 })]; + const issues = await listEmulatorIssues(); + expect(issues.map((i) => i.type)).toContain("limited_stack_vs_sells"); + }); + + it("flags sibling offers that share an order number on the same page", async () => { + state.items = [ + item(1, { orderNumber: 1 }), + item(2, { orderNumber: 1 }), + item(3, { orderNumber: 2 }), + ]; + const issues = await listEmulatorIssues(); + const dup = issues.filter((i) => i.type === "duplicate_order"); + expect(dup.map((i) => i.id).sort()).toEqual([1, 2]); + }); + + it("accepts valid pricing row without flagging", async () => { + state.items = [item(1, { orderNumber: 1 })]; + const issues = await listEmulatorIssues(); + expect(issues).toEqual([]); + }); +}); + +describe("fixEmulatorIssues", () => { + it("deletes orphans, sanitizes pricing and renumbers duplicate orders", async () => { + state.items = [ + item(1, { + pageId: 500, + itemIds: "10834", + pointsType: 101, + costCredits: -5, + costPoints: -1, + amount: 0, + }), + item(2, { limitedStack: 5, limitedSells: 10 }), + item(3, { orderNumber: 1 }), + item(4, { orderNumber: 1 }), + item(5, { orderNumber: 2 }), + ]; + state.orderRows = [ + { id: 3, pageId: 10, orderNumber: 1 }, + { id: 4, pageId: 10, orderNumber: 1 }, + { id: 5, pageId: 10, orderNumber: 2 }, + ]; + + const result = await fixEmulatorIssues(); + + const bySql = (needle: string) => + state.queries.filter((q) => + q.sql.replace(/\s+/g, " ").trimStart().includes(needle), + ); + const deleteItems = bySql("DELETE FROM catalog_items WHERE id IN"); + expect(deleteItems.length).toBe(1); + expect(deleteItems[0].params).toContain(1); + + const pointsFix = bySql( + "UPDATE catalog_items SET points_type = 0 WHERE id IN", + ); + expect(pointsFix.length).toBe(1); + expect(pointsFix[0].params).toContain(1); + + const costFix = bySql("cost_credits = CASE WHEN cost_credits < 0"); + expect(costFix.length).toBe(1); + + const amountFix = bySql("UPDATE catalog_items SET amount = 1 WHERE id IN"); + expect(amountFix.length).toBe(1); + + const limitedFix = bySql( + "UPDATE catalog_items SET limited_stack = limited_sells WHERE id IN", + ); + expect(limitedFix.length).toBe(1); + expect(limitedFix[0].params).toContain(2); + + expect(state.writes).toBeGreaterThan(0); + expect(result.detail.length).toBeGreaterThan(0); + }); + + it("does not write when there is nothing to fix", async () => { + state.items = [item(1, { orderNumber: 1 })]; + const result = await fixEmulatorIssues(); + expect(result.fixed).toBe(0); + expect(state.writes).toBe(0); + }); +}); diff --git a/src/lib/services/furni-maintenance.ts b/src/lib/services/furni-maintenance.ts index 0538eabf..b60958df 100644 --- a/src/lib/services/furni-maintenance.ts +++ b/src/lib/services/furni-maintenance.ts @@ -113,6 +113,7 @@ export interface FurniHealth { catalogDuplicates: number; catalogBcDuplicates: number; catalogPageDuplicates: number; + emulatorIssues: number; } /** Read-only snapshot of how far the furniture tables have drifted. */ @@ -153,6 +154,9 @@ export async function getFurniHealth(): Promise { GROUP BY caption_save, parent_id HAVING COUNT(*) > 1 ) g`); + // ── Emulator-level catalog validation ──────────────────────────── + const emulatorIssues = await countEmulatorIssues(); + const furniData = (await readFurniData()) as Record< string, { furnitype?: Array<{ id?: unknown; classname?: unknown }> } @@ -185,6 +189,7 @@ export async function getFurniHealth(): Promise { catalogDuplicates: Number(cd?.c ?? 0), catalogBcDuplicates: Number(cdb?.c ?? 0), catalogPageDuplicates: Number(cpd?.c ?? 0), + emulatorIssues, }; } @@ -192,6 +197,282 @@ export interface FixSpriteResult extends SpriteIdVerifyResult {} export interface FixOfferResult extends CatalogOfferRebuildResult {} export interface ReconcileResult extends FurniDataReconcileResult {} +/** Points-type values the Arcturus emulator accepts for catalog pricing. */ +const EMULATOR_POINTS_TYPES = new Set([0, 5]); + +export interface EmulatorCatalogIssue { + type: + | "points_currency" + | "negative_cost" + | "nonpositive_amount" + | "limited_stack_vs_sells" + | "missing_page" + | "missing_item" + | "duplicate_order"; + id: number; + pageId?: number; + detail: string; +} + +/** + * Scan catalog_items for the same data problems the Arcturus emulator throws + * on catalog load: unsupported points currency, negative prices, non-positive + * amount, limited_stack below limited_sells, orphaned page/item references and + * duplicate sibling order numbers on the same page. + */ +export async function countEmulatorIssues(): Promise { + const issues = await listEmulatorIssues(); + return issues.length; +} + +export async function listEmulatorIssues(): Promise { + const [items] = (await db.execute(sql` + SELECT + ci.id, ci.page_id AS pageId, ci.item_ids AS itemIds, + ci.cost_credits AS costCredits, ci.cost_points AS costPoints, + ci.points_type AS pointsType, ci.amount, ci.limited_stack AS limitedStack, + ci.limited_sells AS limitedSells, ci.order_number AS orderNumber + FROM catalog_items ci + `)) as unknown as [ + Array<{ + id: number; + pageId: number | string; + itemIds: string; + costCredits: number; + costPoints: number; + pointsType: number; + amount: number; + limitedStack: number; + limitedSells: number; + orderNumber: number; + }>, + unknown, + ]; + const [pages] = (await db.execute( + sql`SELECT id FROM catalog_pages`, + )) as unknown as [Array<{ id: number }>, unknown]; + const [bases] = (await db.execute( + sql`SELECT id FROM items_base`, + )) as unknown as [Array<{ id: number }>, unknown]; + + const pageIds = new Set(pages.map((r) => Number(r.id))); + const baseIds = new Set(bases.map((r) => Number(r.id))); + + const issues: EmulatorCatalogIssue[] = []; + for (const row of items) { + const id = Number(row.id); + const pageId = Number(row.pageId); + + if (!pageIds.has(pageId)) { + issues.push({ + type: "missing_page", + id, + pageId, + detail: `catalog_items #${id} references offer page ${pageId} which does not exist`, + }); + } + for (const part of String(row.itemIds ?? "").split(";")) { + const baseId = Number(part.trim()); + if (baseId > 0 && !baseIds.has(baseId)) { + issues.push({ + type: "missing_item", + id, + pageId, + detail: `catalog_items #${id} references item definition ${baseId} which does not exist`, + }); + } + } + if (!EMULATOR_POINTS_TYPES.has(Number(row.pointsType))) { + issues.push({ + type: "points_currency", + id, + pageId, + detail: `catalog_items #${id} uses unsupported points currency ${row.pointsType}`, + }); + } + if (Number(row.costCredits) < 0 || Number(row.costPoints) < 0) { + issues.push({ + type: "negative_cost", + id, + pageId, + detail: `catalog_items #${id} has negative credits/points cost`, + }); + } + if (Number(row.amount) <= 0) { + issues.push({ + type: "nonpositive_amount", + id, + pageId, + detail: `catalog_items #${id} has amount ${row.amount} (must be positive)`, + }); + } + if (Number(row.limitedStack) < Number(row.limitedSells)) { + issues.push({ + type: "limited_stack_vs_sells", + id, + pageId, + detail: `catalog_items #${id} limited stack ${row.limitedStack} is lower than the live slot count ${row.limitedSells}`, + }); + } + } + + // Duplicate sibling order numbers grouped by (page_id, order_number). + const orderGroups = new Map(); + const rootOrders = new Map(); + for (const row of items) { + const pageId = Number(row.pageId); + const order = Number(row.orderNumber); + const key = pageId <= 0 ? `root:${order}` : `${pageId}:${order}`; + const target = pageId <= 0 ? rootOrders : orderGroups; + const arr = target.get(key) ?? []; + arr.push(Number(row.id)); + target.set(key, arr); + } + const merged = new Map(); + for (const [k, v] of orderGroups) merged.set(k, v); + for (const [k, v] of rootOrders) merged.set(k, v); + for (const [, ids] of merged) { + if (ids.length > 1) { + for (const id of ids) { + issues.push({ + type: "duplicate_order", + id, + detail: `catalog_items #${id} shares an order number with siblings on the same page`, + }); + } + } + } + + return issues; +} + +export interface FixEmulatorResult { + fixed: number; + removed: number; + detail: string[]; +} + +/** + * Fix every emulator-level catalog defect in one transaction: + * - unsupported points currency -> duckets (0) + * - negative credits / points -> 0 + * - amount <= 0 -> 1 + * - limited_stack < limited_sells -> limited_stack = limited_sells + * - orphaned page / item refs -> row deleted + * - duplicate sibling order -> re-numbered sequentially per page + */ +export async function fixEmulatorIssues(): Promise { + const issues = await listEmulatorIssues(); + const detail: string[] = []; + let fixed = 0; + let removed = 0; + + await db.transaction(async (tx) => { + // Rows with a missing page or item reference: the emulator cannot + // render them and every other validation below is meaningless, so delete. + const orphanIds = issues + .filter((i) => i.type === "missing_page" || i.type === "missing_item") + .map((i) => i.id); + if (orphanIds.length > 0) { + await tx.execute( + sql`DELETE FROM catalog_items_limited WHERE catalog_item_id IN (${sql.join([...new Set(orphanIds)], sql`, `)})`, + ); + await tx.execute( + sql`DELETE FROM catalog_items WHERE id IN (${sql.join([...new Set(orphanIds)], sql`, `)})`, + ); + removed = new Set(orphanIds).size; + detail.push( + `Deleted ${removed} offer(s) with missing page/item references`, + ); + } + + // points currency -> duckets + const badPoints = issues.filter((i) => i.type === "points_currency"); + if (badPoints.length > 0) { + await tx.execute( + sql`UPDATE catalog_items SET points_type = 0 WHERE id IN (${sql.join( + badPoints.map((i) => i.id), + sql`, `, + )})`, + ); + fixed += badPoints.length; + detail.push( + `Reset points currency to duckets on ${badPoints.length} offer(s)`, + ); + } + + // negative credits / points -> 0 + const negativeCost = issues.filter((i) => i.type === "negative_cost"); + if (negativeCost.length > 0) { + const ids = negativeCost.map((i) => i.id); + await tx.execute( + sql`UPDATE catalog_items SET cost_credits = CASE WHEN cost_credits < 0 THEN 0 ELSE cost_credits END, cost_points = CASE WHEN cost_points < 0 THEN 0 ELSE cost_points END WHERE id IN (${sql.join(ids, sql`, `)})`, + ); + fixed += ids.length; + detail.push(`Clamped negative prices to 0 on ${ids.length} offer(s)`); + } + + // amount <= 0 -> 1 + const badAmount = issues.filter((i) => i.type === "nonpositive_amount"); + if (badAmount.length > 0) { + await tx.execute( + sql`UPDATE catalog_items SET amount = 1 WHERE id IN (${sql.join( + badAmount.map((i) => i.id), + sql`, `, + )})`, + ); + fixed += badAmount.length; + detail.push(`Set amount to 1 on ${badAmount.length} offer(s)`); + } + + // limited_stack < limited_sells -> limited_stack = limited_sells + const limited = issues.filter((i) => i.type === "limited_stack_vs_sells"); + if (limited.length > 0) { + await tx.execute( + sql`UPDATE catalog_items SET limited_stack = limited_sells WHERE id IN (${sql.join( + limited.map((i) => i.id), + sql`, `, + )}) AND limited_stack < limited_sells`, + ); + fixed += limited.length; + detail.push( + `Raised limited stack to the sold count on ${limited.length} offer(s)`, + ); + } + + // duplicate sibling order -> re-number sequentially per page + const dupOrders = issues.filter((i) => i.type === "duplicate_order"); + if (dupOrders.length > 0) { + const [allRows] = (await tx.execute(sql` + SELECT id, page_id AS pageId, order_number AS orderNumber + FROM catalog_items ORDER BY page_id, order_number, id + `)) as unknown as [ + Array<{ id: number; pageId: number | string; orderNumber: number }>, + unknown, + ]; + let lastPage: number | null = null; + let next = 1; + for (const row of allRows) { + const page = Number(row.pageId); + if (page !== lastPage) { + lastPage = page; + next = 1; + } + const newOrder = next++; + if (newOrder !== Number(row.orderNumber)) { + await tx.execute( + sql`UPDATE catalog_items SET order_number = ${newOrder} WHERE id = ${Number(row.id)}`, + ); + } + } + fixed += dupOrders.length; + detail.push(`Re-numbered sibling order numbers on affected pages`); + } + }); + + return { fixed, removed, detail }; +} + /** Fix items_base.sprite_id -> id (only where furnidata agrees). */ export async function fixSpriteIds(): Promise { return verifyAndFixSpriteIds(); diff --git a/src/lib/services/item-kind.test.ts b/src/lib/services/item-kind.test.ts index bdb0e86e..7c59a5d3 100644 --- a/src/lib/services/item-kind.test.ts +++ b/src/lib/services/item-kind.test.ts @@ -48,6 +48,16 @@ describe("classifyCatalogItem", () => { expect(item({ item_name: "post_it" })).toBe("sticky"); expect(item({ item_name: "postit" })).toBe("sticky"); }); + it("classifies posters and currency furniture", () => { + expect(item({ item_name: "poster100" })).toBe("poster"); + expect(item({ item_name: "poster5000" })).toBe("poster"); + expect(item({ item_name: "funtastic_four_poster" })).toBe("poster"); + expect(item({ item_name: "duckfountain" })).toBe("poster"); + expect(item({ item_name: "Swimming" })).toBe("poster"); + expect(item({ item_name: "CF_50000_Hucci" })).toBe("currency"); + expect(item({ item_name: "DF_102_10_bluechip" })).toBe("currency"); + expect(item({ item_name: "sofa" })).toBe("furni"); + }); it("classifies everything else as furni", () => { expect(item({})).toBe("furni"); }); diff --git a/src/lib/services/item-kind.ts b/src/lib/services/item-kind.ts index f76e4dc5..eb3fc4c4 100644 --- a/src/lib/services/item-kind.ts +++ b/src/lib/services/item-kind.ts @@ -10,6 +10,12 @@ * assets live in the pet asset pipeline (`public/nitro-assets/bundled/pet`), * not the furniture bundle dirs. * - **effect / bot / sticky** — system items with no furniture visuals at all. + * - **poster** — poster furniture (`poster*` classnames, wall posters, novelty + * poster furni). Renders through the poster imager, so no `.nitro` / `_icon.png` + * assets are expected. + * - **currency** — credit/pixel furniture (`CF_*`, `DF_*`). Renders through the + * currency imager (credits coin, badge-format exchange stickers), so no + * `.nitro` / `_icon.png` assets are expected. * - **furni** — everything else; must ship a `.nitro` and `_icon.png`. */ @@ -19,6 +25,8 @@ export type CatalogItemKind = | "effect" | "bot" | "sticky" + | "poster" + | "currency" | "furni"; export interface CatalogItemLike { @@ -42,6 +50,24 @@ export function isPetInteraction(interactionType?: string | null): boolean { return /^pet\d+$/i.test((interactionType ?? "").trim()); } +/** Poster furniture classnames that have no `.nitro` asset expectations. */ +export function isPosterClassname(itemName: string): boolean { + const name = itemName.trim(); + if (/^poster\d+$/i.test(name)) return true; + const POSTER_SPECIAL = new Set([ + "funtastic_four_poster", + "duckfountain", + "duckfountain2", + "swimming", + ]); + return POSTER_SPECIAL.has(name.toLowerCase()); +} + +/** Credit/pixel furniture (`CF_*`, `DF_*`) ships no `.nitro` assets. */ +export function isCurrencyClassname(itemName: string): boolean { + return /^(?:cf|df)_/i.test(itemName.trim()); +} + /** Classify an `items_base` row into a catalog item kind. */ export function classifyCatalogItem(item: CatalogItemLike): CatalogItemKind { const name = item.item_name.trim(); @@ -52,6 +78,8 @@ export function classifyCatalogItem(item: CatalogItemLike): CatalogItemKind { if (/^avatar_effect/i.test(name)) return "effect"; if (/^(bot_|rentable_bot_)/i.test(name)) return "bot"; if (/^post[._]?it/i.test(name)) return "sticky"; + if (isPosterClassname(name)) return "poster"; + if (isCurrencyClassname(name)) return "currency"; return "furni"; } @@ -62,6 +90,8 @@ export const NO_ASSET_KINDS: ReadonlySet = new Set([ "effect", "bot", "sticky", + "poster", + "currency", ]); /** Kinds that are not purchasable furniture and should not get catalog entries. */ diff --git a/update-Nitrov3.sh b/update-Nitrov3.sh deleted file mode 100755 index 12d4c125..00000000 --- a/update-Nitrov3.sh +++ /dev/null @@ -1,3810 +0,0 @@ -#!/bin/bash -set -Eeo pipefail - -# ╔══════════════════════════════════════════════════════════════════════════════╗ -# ║ SMaRT — Emulator / Octane / Octane-Renderer ║ -# ║ Updater & Manager by Remco ║ -# ╚══════════════════════════════════════════════════════════════════════════════╝ - -# ============================================================================= -# CONSTANTS -# ============================================================================= -readonly SCRIPT_VERSION="8.0.4" -readonly SCRIPT_BUILD="20260830" -readonly SCRIPT_NAME="$(basename "$0")" -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -readonly START_TIME=$(date +%s) -readonly LOCK_FILE="/tmp/${SCRIPT_NAME%.sh}.lock" - -# ============================================================================= -# COLORS -# ============================================================================= -if [ -t 1 ] && command -v tput &>/dev/null && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then - C_RESET="\033[0m" C_BOLD="\033[1m" C_DIM="\033[2m" - C_RED="\033[1;31m" C_GREEN="\033[1;32m" C_YELLOW="\033[1;33m" - C_BLUE="\033[1;34m" C_MAGENTA="\033[1;35m" C_CYAN="\033[1;36m" - C_WHITE="\033[1;37m" C_GOLD="\033[38;5;220m" - C_BG_RED="\033[41m" C_BG_GREEN="\033[42m" - C_BG_BLUE="\033[44m" C_BG_MAGENTA="\033[45m" C_BG_CYAN="\033[46m" -else - C_RESET="" C_BOLD="" C_DIM="" - C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_MAGENTA="" C_CYAN="" C_WHITE="" C_GOLD="" - C_BG_RED="" C_BG_GREEN="" C_BG_BLUE="" C_BG_MAGENTA="" C_BG_CYAN="" -fi - -# ============================================================================= -# EMOJIS -# ============================================================================= -if locale charmap 2>/dev/null | grep -qi utf; then - E_OK="✔" E_FAIL="✘" E_WARN="⚠" E_ARROW="➜" E_DOT="●" - E_GEAR="⚙" E_ROCKET="🚀" E_SHIELD="🛡" E_DB="🗄" E_GIT="🔀" - E_WRENCH="🔧" E_CHART="📊" E_BOLT="⚡" E_BROOM="🧹" E_EYE="👁" - E_LOCK="🔒" E_HEART="♥" E_CLOUD="☁" E_FIRE="🔥" E_GLOBE="🌐" - E_CONSOLE="🖥" E_CLOCK="⏱" E_DOCKER="🐳" E_CHECK="🔍" -else - E_OK="[OK]" E_FAIL="[!]" E_WARN="[!]" E_ARROW="->" E_DOT="*" - E_GEAR="[G]" E_ROCKET="[R]" E_SHIELD="[S]" E_DB="[DB]" E_GIT="[G]" - E_WRENCH="[W]" E_CHART="[C]" E_BOLT="[!]" E_BROOM="[C]" E_EYE="[E]" - E_LOCK="[L]" E_HEART="[H]" E_CLOUD="[C]" E_FIRE="[F]" E_GLOBE="[W]" - E_CONSOLE="[M]" E_CLOCK="[T]" E_DOCKER="[D]" E_CHECK="[C]" -fi - -# ============================================================================= -# STATE -# ============================================================================= -SELECTIVE_UPDATE="" -DRY_RUN=false -HAD_UPDATES=false -NITRO_BUILT=false -EMULATOR_BUILT=false -CMS_BUILT=false -NGINX_CHANGED=false -ERRORS=0 -WARNINGS=0 -UPDATED_REPOS=() -LAST_BACKUP="" -ROLLBACK_NEEDED=false -BINARY_BACKUP_DIR="" -NOTIFY_FAILED=false -CONFIG_BACKUP_DIR="" -MAVEN_OPTS="${MAVEN_OPTS:--Xmx2G}" - -# ============================================================================= -# LOAD .ENV -# ============================================================================= -if [ -f "$SCRIPT_DIR/.env" ]; then set -a; . "$SCRIPT_DIR/.env"; set +a; fi - -if [ -z "${NITRO_DB_NAME:-}" ] && [ -n "${DATABASE_URL:-}" ]; then - eval "$( - python3 -c " -import os, urllib.parse -url = os.environ['DATABASE_URL'] -parsed = urllib.parse.urlparse(url) -auth = parsed.netloc.rsplit('@', 1)[0] if '@' in parsed.netloc else '' -user, pw = (auth.split(':', 1) + ['', ''])[:2] -host = parsed.hostname or '127.0.0.1' -port = parsed.port or 3306 -name = parsed.path.lstrip('/').split('?')[0] -print(f'DB_USER={user!r}') -print(f'DB_PASS={urllib.parse.unquote(pw)!r}') -print(f'DB_HOST={host!r}') -print(f'DB_PORT={port!r}') -print(f'DB_NAME={name!r}') -" 2>/dev/null || true - )" -fi - -# ============================================================================= -# CONFIG -# ============================================================================= -LOG_DIR="${NITRO_LOG_DIR:-$SCRIPT_DIR/storage/logs}" -mkdir -p "$LOG_DIR" 2>/dev/null || true -LOG_FILE="$LOG_DIR/update-$(date +%Y%m%d-%H%M%S).log" -NOTIFY_URL="${NITRO_NOTIFY_URL:-}" -DB_NAME="${NITRO_DB_NAME:-${DB_NAME:-habbo}}" -DB_HOST="${NITRO_DB_HOST:-${DB_HOST:-127.0.0.1}}" -DB_PORT="${NITRO_DB_PORT:-${DB_PORT:-3306}}" -DB_USER="${NITRO_DB_USER:-${DB_USER:-root}}" -DB_PASS="${NITRO_DB_PASS:-${DB_PASS:-}}" -EMULATOR_SERVICE="${NITRO_EMULATOR_SERVICE:-emulator}" -EMULATOR_DIR="${NITRO_EMULATOR_PATH:-/var/www/emulator}" -# Polaris-Emulator: the git repository root is $EMULATOR_DIR itself, while the -# Maven project (pom.xml + target/) lives in the $EMULATOR_DIR/Emulator submodule. -EMULATOR_REPO="${NITRO_EMULATOR_REPO:-$EMULATOR_DIR}" -EMULATOR_MODULE="${NITRO_EMULATOR_MODULE:-$EMULATOR_DIR/Emulator}" -SQL_DIR="${NITRO_SQL_DIR:-$EMULATOR_DIR/Database/Database Updates}" -GAMEDATA_CONF_DIR="${NITRO_GAMEDATA_DIR:-/var/www/Gamedata/config}" -NITRO_SRC_DIR="${NITRO_CLIENT_DIR:-/var/www/Octane/public/configuration}" -BACKUP_DIR="${NITRO_BACKUP_DIR:-$EMULATOR_DIR/Database/Database Updates/backups}" -NITRO_CLIENT="${NITRO_CLIENT_SRC:-/var/www/Octane}" -NITRO_RENDERER="${NITRO_RENDERER_SRC:-/var/www/Octane-Renderer}" -NITRO_BRANCH="${NITRO_BRANCH:-}" -# CMS (EpicNextCMS / atom-nexst) — the Next.js backend that serves the hotel. -# It lives next to this script. We never git-stash it automatically (it may -# carry uncommitted local work); see NITRO_CMS_PULL to allow opt-in pulls. -CMS_DIR="${NITRO_CMS_DIR:-$SCRIPT_DIR}" -CMS_PM2_NAME="${NITRO_CMS_PM2_NAME:-epic-next-app}" -CMS_PORT="${NITRO_CMS_PORT:-3002}" -CMS_INSTALL_CMD="${NITRO_CMS_INSTALL_CMD:-pnpm install --frozen-lockfile}" -CMS_BUILD_CMD="${NITRO_CMS_BUILD_CMD:-pnpm build}" -CMS_HEALTH_RETRIES="${NITRO_CMS_HEALTH_RETRIES:-18}" -CMS_HEALTH_INTERVAL="${NITRO_CMS_HEALTH_INTERVAL:-5}" -# 1 = also git-pull the CMS before building (stashes local work if dirty). -CMS_PULL="${NITRO_CMS_PULL:-0}" -# Dedicated per-repo yarn cache folders. The renderer + client installs run in -# parallel and yarn 1.22 has no cache locking, so a single shared cache gets -# corrupted and node_modules is left incomplete (vite/pixi.js missing) while -# yarn still exits 0. Isolating each repo's cache via --cache-folder prevents -# that race entirely. -# GitHub repository URLs for auto-setup (used when repos are missing) -GIT_REPO_CLIENT="${NITRO_GIT_CLIENT:-https://github.com/duckietm/Octane.git}" -GIT_REPO_RENDERER="${NITRO_GIT_RENDERER:-https://github.com/duckietm/Octane-Renderer.git}" -YARN_CACHE_DIR="${NITRO_YARN_CACHE_DIR:-/var/www/.yarn-cache}" -mkdir -p "$YARN_CACHE_DIR" 2>/dev/null || true -MIN_DISK_GB="${NITRO_MIN_DISK_GB:-5}" -HEALTH_RETRIES="${NITRO_HEALTH_RETRIES:-12}" -HEALTH_INTERVAL="${NITRO_HEALTH_INTERVAL:-5}" -NITRO_SSL_VERIFY="${NITRO_SSL_VERIFY:-OFF}" -CONFIG_BACKUP_DIR="${SCRIPT_DIR}/storage/config-backups" -mkdir -p "$CONFIG_BACKUP_DIR" 2>/dev/null || true -mkdir -p "$BACKUP_DIR" 2>/dev/null || true -NITRO_LANG="${NITRO_LANG:-en}" -# Nginx site config(s) that serve /gamedata/. After every build/rebuild the -# update script (re)adds an exact-match redirect so /gamedata/ points at -# /gamedata/config/ instead of the raw directory listing. -NGINX_SITE_CONFS=( - "${NITRO_NGINX_SITE_CONF:-/etc/nginx/sites-enabled/cms.conf}" - "${NITRO_NGINX_SITE_CONF_AVAILABLE:-/etc/nginx/sites-available/cms.conf}" -) - -MYSQL_CRED="-h $DB_HOST -P $DB_PORT -u $DB_USER --ssl-verify-server-cert=${NITRO_SSL_VERIFY}" -[ -n "$DB_PASS" ] && MYSQL_CRED="$MYSQL_CRED --password=$DB_PASS" - -if command -v mariadb &>/dev/null; then - MYSQL_CLIENT="mariadb"; MYSQL_DUMP="mariadb-dump" -elif command -v mysql &>/dev/null; then - MYSQL_CLIENT="mysql"; MYSQL_DUMP="mysqldump" -else - MYSQL_CLIENT="mariadb"; MYSQL_DUMP="mariadb-dump" -fi - -# ============================================================================= -# LANGUAGE / INTERNATIONALIZATION -# ============================================================================= -declare -A _T - -_t() { - local key="$1"; shift - local text="${_T[$key]:-$key}" - for arg in "$@"; do - text="${text/\%s/$arg}" - done - echo "$text" -} - -load_language() { - local lang="${NITRO_LANG:-en}" - case "${lang,,}" in - nl) _T=( -# ── MENU ────────────────────────────────────────────── -["UPDATE"]="UPDATE" -["TOOLS"]="TOOLS" -["ADVANCED"]="ADVANCED" -["Quick Update — current branch"]="Snelle Update — huidige branch" -["Full Update (choose branch)"]="Volledige Update (kies branch)" -["Emulator Only"]="Alleen Emulator" -["Octane Client Only"]="Alleen Octane Client" -["Renderer Only"]="Alleen Renderer" -["Configs Only"]="Alleen Configuraties" -["Dev Mode (skip builds, only configs)"]="Dev Modus (skip builds, alleen configs)" -["Switch Branch"]="Wissel Branch" -["Database"]="Database" -["Backup / Restore"]="Backup / Herstel" -["Services"]="Services" -["Status"]="Status" -["Health Check"]="Gezondheidscheck" -["Clean"]="Opschonen" -["Logs"]="Logboeken" -["Config"]="Configuratie" -["Git Log"]="Git Logboek" -["Compare Branches"]="Vergelijk Branches" -["Cron Scheduler"]="Cron Planner" -["View Git Stashes"]="Bekijk Git Stashes" -["Exit"]="Afsluiten" -["Flyaway Repair"]="Vliegende Reparatie" -["Auto-setup missing repos"]="Automatisch ontbrekende repos installeren" -["Cleanup old Nitro dirs"]="Oude Nitro mappen opruimen" -["Rebuild Frontend"]="Frontend Herbouwen" -["Rebuild Emulator"]="Emulator Herbouwen" -["Select [0-24]"]="Selecteer [0-24]" -["Fix Client Links (icons/furniture/gamedata)"]="Fixeer Client Links (icons/meubels/gamedata)" -["Press Enter to continue..."]="Druk op Enter om door te gaan..." -["Goodbye!"]="Tot ziens!" -# ── HELP ────────────────────────────────────────────── -["SMaRT — Emulator / Octane / Octane-Renderer Updater & Manager"]="SMaRT — Emulator / Octane / Octane-Renderer Updater & Beheerder" -["USAGE:"]="GEBRUIK:" -["COMMANDS:"]="COMMANDO'S:" -["Open menu (default)"]="Open menu (standaard)" -["Run update"]="Voer update uit" -["Create database backup"]="Maak databasebackup" -["Restore from backup"]="Herstel vanuit backup" -["List backups"]="Toon backups" -["System dashboard"]="Systeemdashboard" -["Health check"]="Gezondheidscheck" -["Service manager"]="Servicebeheerder" -["Database tools"]="Database tools" -["Show config"]="Toon configuratie" -["View logs"]="Bekijk logboeken" -["Cache cleanup"]="Cache opschonen" -["Git history"]="Git geschiedenis" -["Branch diff"]="Branch vergelijking" -["Cron setup"]="Cron instellen" -["View git stashes"]="Bekijk git stashes" -["Branch (auto-detects)"]="Branch (auto-detectie)" -["Site URL"]="Site URL" -["emulator|client|renderer|configs|dev"]="emulator|client|renderer|configs|dev" -["Preview only (no changes)"]="Alleen voorbeeld (geen wijzigingen)" -["Show this help"]="Toon deze hulp" -["Show version"]="Toon versie" -["OPTIONS:"]="OPTIES:" -["Unknown:"]="Onbekend:" -# ── STEP HEADERS ────────────────────────────────────── -["Update & Build Emulator"]="Update & Bouw Emulator" -["Update Octane-Renderer"]="Update Octane-Renderer" -["Update & Build Octane"]="Update & Bouw Octane" -["Sync Configurations"]="Synchroniseer Configuraties" -["Cleanup"]="Opschonen" -["Set Permissions"]="Stel Permissies in" -["Restart Services"]="Herstart Services" -["System Diagnostics"]="Systeemdiagnose" -["Database Backup"]="Databasebackup" -["Database Restore"]="Database Herstel" -["Backup Inventory"]="Backup Inventaris" -["System Status"]="Systeemstatus" -["Health Check"]="Gezondheidscheck" -["Service Manager"]="Servicebeheer" -["Database Tools"]="Database Tools" -["Configuration"]="Configuratie" -["Log Viewer"]="Logboekweergave" -["Cache Cleanup"]="Cache Opschonen" -["Git Commit History"]="Git Commit Geschiedenis" -["Branch Comparison"]="Branch Vergelijking" -["Cron Scheduler"]="Cron Planner" -["Git Stashes"]="Git Stashes" -# ── PROMPTS / CONFIRM ───────────────────────────────── -["Start update?"]="Update starten?" -["Continue?"]="Doorgaan?" -["Restore %s? OVERWRITES database!"]="Herstel %s? OVERSCHRIJFT database!" -["Select [1-%s]"]="Selecteer [1-%s]" -["Branch [1-%s]"]="Branch [1-%s]" -["Name:"]="Naam:" -["Custom"]="Aangepast" -["Select:"]="Selecteer:" -["SQL:"]="SQL:" -["Minute [0-59]:"]="Minuut [0-59]:" -["Hour [0-23]:"]="Uur [0-23]:" -["Branch A:"]="Branch A:" -["Branch B:"]="Branch B:" -["(current)"]="(huidig)" -# ── INFO / OK / WARN / FAIL ────────────────────────── -["All commands available"]="Alle commando's beschikbaar" -["All directories exist"]="Alle mappen bestaan" -["Database connected"]="Database verbonden" -["Emulator: already up to date"]="Emulator: al up-to-date" -["Renderer: already up to date"]="Renderer: al up-to-date" -["Octane: already up to date"]="Octane: al up-to-date" -["Emulator JAR backed up"]="Emulator JAR geback-upt" -["Octane dist backed up"]="Octane dist geback-upt" -["Emulator JAR restored"]="Emulator JAR hersteld" -["Octane dist restored"]="Octane dist hersteld" -["Renderer dependencies installed"]="Renderer afhankelijkheden geïnstalleerd" -["No repo updates; skipping parallel builds"]="Geen repo-updates; parallel bouwen overgeslagen" -["Dev mode: skipping builds, syncing configs only"]="Dev modus: builds overgeslagen, alleen configs synchroniseren" -["Config back-up: %s"]="Config-backup: %s" -["%s config file(s) synced"]="%s configuratiebestand(en) gesynchroniseerd" -["All config files valid"]="Alle configuratiebestanden geldig" -["%s config(s) invalid"]="%s configuratie(s) ongeldig" -["All config URLs synced"]="Alle config-URL's gesynchroniseerd" -["Redis cache flushed"]="Redis cache geleegd" -["Permissions set"]="Permissies ingesteld" -["Jar: %s"]="Jar: %s" -["Launch script updated"]="Startscript bijgewerkt" -["%s restarted"]="%s herstart" -["Emulator health check passed (service up + port listening)"]="Emulator gezondheidscheck geslaagd (service actief + poort luistert)" -["Emulator not healthy after %s retries"]="Emulator niet gezond na %s pogingen" -["Nginx reloaded"]="Nginx herladen" -["Redis flushed"]="Redis geleegd" -["Cleanup done (%s logs removed)"]="Opschonen voltooid (%s logs verwijderd)" -["Old config backups pruned"]="Oude config-backups opgeruimd" -["Dry-run: would update repos, run DB backups, build, sync configs, cleanup, restart services"]="Proefrun: zou repos updaten, DB-backups maken, bouwen, configs synchroniseren, opschonen, services herstarten" -["Backup: %s — %s"]="Backup: %s — %s" -["Restored"]="Hersteld" -["Cancelled"]="Geannuleerd" -["Started"]="Gestart" -["Stopped"]="Gestopt" -["Restarted"]="Herstart" -["Yarn cleaned"]="Yarn opgeschoond" -["Next.js cache cleared"]="Next.js cache gewist" -["Logs cleaned"]="Logs opgeschoond" -["Full cleanup"]="Volledig opgeschoond" -["Docker pruned"]="Docker opgeruimd" -["Optimized"]="Geoptimaliseerd" -["Old Maven artifacts cleaned"]="Oude Maven artefacten opgeruimd" -["Scheduled daily 03:00"]="Ingesteld dagelijks 03:00" -["Scheduled daily 04:00"]="Ingesteld dagelijks 04:00" -["Scheduled weekly"]="Ingesteld wekelijks" -["Scheduled %s"]="Ingesteld %s" -["Removed"]="Verwijderd" -["Branch: %s"]="Branch: %s" -["No backup dir"]="Geen backup-map" -["No backups"]="Geen backups" -["No logs"]="Geen logboeken" -["No log dir"]="Geen logboek-map" -["No stashes found"]="Geen stashes gevonden" -["Not a git repo"]="Geen git-repository" -["Invalid"]="Ongeldig" -["Invalid selection"]="Ongeldige selectie" -["%s updates, %s warnings"]="%s updates, %s waarschuwingen" -["Update had errors"]="Update had fouten" -["Dev mode had errors"]="Dev modus had fouten" -["Invalid, using: %s"]="Ongeldig, gebruik: %s" -["Backup: %s — %s"]="Backup: %s — %s" -["Script:"]="Script:" -["Bash syntax"]="Bash syntax" -["Configs:"]="Configuraties:" -["Config dir"]="Config map" -["All JSON valid"]="Alle JSON geldig" -["JSON errors:"]="JSON fouten:" -["Gamedata config"]="Gamedata config" -["Backup dir"]="Backup map" -["Network:"]="Netwerk:" -["Nginx config"]="Nginx configuratie" -["Redis PING"]="Redis PING" -["Site reachable"]="Site bereikbaar" -["Emulator port open"]="Emulator poort open" -["Emulator port closed"]="Emulator poort gesloten" -["Databases:"]="Databases:" -["MySQL connect"]="MySQL verbinding" -["Disk:"]="Schijf:" -["Memory:"]="Geheugen:" -["Load:"]="Belasting:" -["Commands:"]="Commando's:" -["Bashisms"]="Bashisms" -["Tables:"]="Tabellen:" -# ── DIE MESSAGES ────────────────────────────────────── -["Another instance is already running (lock: %s)"]="Er draait al een andere instantie (lock: %s)" -["Required commands missing"]="Vereiste commando's ontbreken" -["Missing: %s"]="Ontbreekt: %s" -["Missing dir: %s"]="Map ontbreekt: %s" -["Only %sGB free (min %sGB)"]="Slechts %sGB vrij (min %sGB)" -["Low disk: %sGB free"]="Weinig schijfruimte: %sGB vrij" -["%sGB disk available"]="%sGB schijf beschikbaar" -["DB connection failed"]="Databaseverbinding mislukt" -["Maven build failed (see: %s)"]="Maven build mislukt (zie: %s)" -["No jar found"]="Geen jar gevonden" -["yarn install failed"]="yarn install mislukt" -["yarn build failed (see: %s)"]="yarn build mislukt (zie: %s)" -["Build failure in: %s"]="Buildfout in: %s" -["Backup failed"]="Backup mislukt" -["Restore failed"]="Herstel mislukt" -# ── SECTIONS ────────────────────────────────────────── -[" UPDATE "]=" UPDATE " -[" TOOLS "]=" TOOLS " -[" ADVANCED "]=" GEAVANCEERD " -# ── SUBMENUS ────────────────────────────────────────── -["Start Emulator"]="Start Emulator" -["Stop Emulator"]="Stop Emulator" -["Restart Emulator"]="Herstart Emulator" -["Start Nginx"]="Start Nginx" -["Restart Nginx"]="Herstart Nginx" -["Status All"]="Status Alles" -["Emulator Logs"]="Emulator Logs" -["Emulator Journal"]="Emulator Journal" -["Return"]="Terug" -["DB Info"]="DB Info" -["List Tables"]="Toon Tabellen" -["Table Sizes"]="Tabelgroottes" -["Optimize"]="Optimaliseer" -["Custom Query"]="Eigen Query" -["Active Users"]="Actieve Gebruikers" -["Processlist"]="Proceslijst" -["Yarn"]="Yarn" -["Next.js"]="Next.js" -["Redis"]="Redis" -["Everything"]="Alles" -["Docker"]="Docker" -["Maven"]="Maven" -["Daily 03:00"]="Dagelijks 03:00" -["Daily 04:00"]="Dagelijks 04:00" -["Weekly Sun 03:00"]="Wekelijks Zo 03:00" -["Custom time"]="Eigen tijd" -["Remove"]="Verwijderen" -["Last 20 commits on %s (ahead: %s):"]="Laatste 20 commits op %s (vooruit: %s):" -["%s is %s ahead, %s behind %s"]="%s is %s voor, %s achter op %s" -["Current:"]="Huidig:" -["none"]="geen" -# ── BOX LABELS ──────────────────────────────────────── -["SERVER"]="SERVER" -["RESOURCES"]="BRONNEN" -["SERVICES"]="SERVICES" -["GIT REPOS"]="GIT REPOS" -["SCORE"]="SCORE" -["DB INFO"]="DB INFO" -["CONFIGURATION"]="CONFIGURATIE" -["Host:"]="Host:" -["OS:"]="Besturingssysteem:" -["Uptime:"]="Uptime:" -["CPU:"]="CPU:" -["Memory:"]="Geheugen:" -["Disk:"]="Schijf:" -["Database:"]="Database:" -["Passed:"]="Geslaagd:" -["Failed:"]="Mislukt:" -["Warnings:"]="Waarschuwingen:" -["Name:"]="Naam:" -["Size:"]="Grootte:" -["Tables:"]="Tabellen:" -["Site:"]="Site:" -["Branch:"]="Branch:" -["Emulator:"]="Emulator:" -["Octane:"]="Octane:" -["Renderer:"]="Renderer:" -["Service:"]="Service:" -["Socket:"]="Socket:" -["API:"]="API:" -["Gamedata:"]="Gamedata:" -["Min Disk:"]="Min Schijf:" -# ── SPINNER ─────────────────────────────────────────── -["Building emulator (parallel)..."]="Emulator aan het bouwen (parallel)..." -["Installing deps..."]="Afhankelijkheden installeren..." -["Building frontend..."]="Frontend aan het bouwen..." -["Backing up..."]="Backup aan het maken..." -["Restoring..."]="Aan het herstellen..." -["Optimizing..."]="Aan het optimaliseren..." -# ── NOTIFY ────────────────────────────────────────── -["SUCCESS"]="GELUKT" -["FAILED"]="MISLUKT" -["Completed in %s (%s warnings)"]="Voltooid in %s (%s waarschuwingen)" -# ── OTHER ─────────────────────────────────────────── -["Total: %s backups — %s"]="Totaal: %s backups — %s" -["FILENAME"]="BESTANDSNAAM" -["SIZE"]="GROOTTE" -["All config files valid"]="Alle configuratiebestanden geldig" -# ── SUMMARY ────────────────────────────────────────── -["UPDATE COMPLETED SUCCESSFULLY"]="UPDATE SUCCESVOL VOLTOOID" -["COMPLETED WITH %s ERROR(S)"]="VOLTOOID MET %s FOUT(EN)" -["Duration:"]="Duur:" -["Updates:"]="Updates:" -["Octane build:"]="Octane build:" -["Warnings:"]="Waarschuwingen:" -["Repos:"]="Repos:" -["Yes"]="Ja" -["No"]="Nee" -# ── NEW (v8.1) ──────────────────────────────────────── -["Update & Build CMS"]="Update & Bouw CMS" -["Environment %s is up to date"]="Omgeving %s is up-to-date" -["%s new variable(s) added to %s"]="%s nieuwe variabele(n) toegevoegd aan %s" -["No .env.example in %s — skipping env sync"]="Geen .env.example in %s — env-sync overgeslagen" -["No .env file in %s"]="Geen .env-bestand in %s" -["Duplicate key(s) in %s: %s"]="Dubbele sleutel(s) in %s: %s" -["Missing variable(s) in %s: %s"]="Ontbrekende variabele(n) in %s: %s" -["Invalid .env — fix before next startup"]="Ongeldig .env — corrigeer vóór de volgende start" -["CMS build complete"]="CMS-build voltooid" -["CMS: already up to date"]="CMS: al up-to-date" -["CMS health check passed (port %s listening)"]="CMS gezondheidscheck geslaagd (poort %s luistert)" -["CMS not healthy after %s retries (port %s)"]="CMS niet gezond na %s pogingen (poort %s)" -["%s restarted"]="%s herstart" -["%s already healthy — no restart needed"]="%s al gezond — herstart niet nodig" -["CMS unhealthy — restarting %s"]="CMS niet gezond — %s opnieuw starten" -["No %s/package.json — skipping CMS"]="Geen %s/package.json — CMS overgeslagen" -["CMS git pull disabled (NITRO_CMS_PULL=0) — building from current tree"]="CMS git-pull uitgeschakeld (NITRO_CMS_PULL=0) — bouwen vanuit huidige boom" -["Environment sync & validation..."]="Omgeving synchroniseren & valideren..." -["No .env.example in"]="Geen .env.example in" - ) ;; - fr) _T=( -# ── MENU ────────────────────────────────────────────── -["UPDATE"]="MISE À JOUR" -["TOOLS"]="OUTILS" -["ADVANCED"]="AVANCÉ" -["Quick Update — current branch"]="Mise à jour rapide — branche actuelle" -["Full Update (choose branch)"]="Mise à jour complète (choisir branche)" -["Emulator Only"]="Émulateur uniquement" -["Octane Client Only"]="Client Octane uniquement" -["Renderer Only"]="Rendu uniquement" -["Configs Only"]="Configs uniquement" -["Dev Mode (skip builds, only configs)"]="Mode Dev (sans builds, configs seulement)" -["Switch Branch"]="Changer de branche" -["Database"]="Base de données" -["Backup / Restore"]="Sauvegarde / Restauration" -["Services"]="Services" -["Status"]="Statut" -["Health Check"]="Vérification de santé" -["Clean"]="Nettoyer" -["Logs"]="Journaux" -["Config"]="Configuration" -["Git Log"]="Journal Git" -["Compare Branches"]="Comparer les branches" -["Cron Scheduler"]="Planificateur Cron" -["View Git Stashes"]="Voir les stashs Git" -["Exit"]="Quitter" -["Flyaway Repair"]="Réparation Flyaway" -["Auto-setup missing repos"]="Installation auto des repos manquants" -["Cleanup old Nitro dirs"]="Nettoyer les anciens répertoires Nitro" -["Rebuild Frontend"]="Reconstruire le Frontend" -["Rebuild Emulator"]="Reconstruire l'Emulateur" -["Select [0-24]"]="Sélectionner [0-24]" -["Fix Client Links (icons/furniture/gamedata)"]="Corriger les liens Client (icônes/meubles/gamedata)" -["Press Enter to continue..."]="Appuyez sur Entrée pour continuer..." -["Goodbye!"]="Au revoir !" -["Start update?"]="Lancer la mise à jour ?" -["Continue?"]="Continuer ?" -# ── HELP ────────────────────────────────────────────── -["USAGE:"]="UTILISATION :" -["COMMANDS:"]="COMMANDES :" -["OPTIONS:"]="OPTIONS :" -# ── STEP HEADERS ────────────────────────────────────── -["Update & Build Emulator"]="Mise à jour & Construction Émulateur" -["Update Octane-Renderer"]="Mise à jour Octane-Renderer" -["Update & Build Octane"]="Mise à jour & Construction Octane" -["Sync Configurations"]="Synchroniser les configurations" -["Cleanup"]="Nettoyage" -["Set Permissions"]="Définir les permissions" -["Restart Services"]="Redémarrer les services" -["System Diagnostics"]="Diagnostics système" -["Database Backup"]="Sauvegarde base de données" -["Database Restore"]="Restauration base de données" -["Backup Inventory"]="Inventaire des sauvegardes" -["System Status"]="Statut du système" -["Health Check"]="Vérification de santé" -["Service Manager"]="Gestionnaire de services" -["Database Tools"]="Outils base de données" -["Configuration"]="Configuration" -["Log Viewer"]="Visualisateur de journaux" -["Cache Cleanup"]="Nettoyage du cache" -["Git Commit History"]="Historique des commits Git" -["Branch Comparison"]="Comparaison de branches" -["Cron Scheduler"]="Planificateur Cron" -["Git Stashes"]="Stashs Git" -["Missing: %s"]="Manquant : %s" -["Missing dir: %s"]="Répertoire manquant : %s" -["All commands available"]="Toutes les commandes disponibles" -["All directories exist"]="Tous les répertoires existent" -["Database connected"]="Base de données connectée" -["Maven build failed (see: %s)"]="Build Maven échoué (voir : %s)" -["yarn install failed"]="yarn install échoué" -["yarn build failed (see: %s)"]="yarn build échoué (voir : %s)" -["Jar: %s"]="Jar : %s" -["Backup: %s — %s"]="Sauvegarde : %s — %s" -["Restored"]="Restauré" -["Cancelled"]="Annulé" -["Invalid"]="Invalide" -["Invalid selection"]="Sélection invalide" -["Scheduled daily 03:00"]="Planifié quotidien 03:00" -["Scheduled daily 04:00"]="Planifié quotidien 04:00" -["Scheduled weekly"]="Planifié hebdomadaire" -["Removed"]="Supprimé" -["Return"]="Retour" -["none"]="aucun" -["Yes"]="Oui" -["No"]="Non" -["Goodbye!"]="Au revoir !" - ) ;; - es) _T=( -# ── MENU ────────────────────────────────────────────── -["UPDATE"]="ACTUALIZAR" -["TOOLS"]="HERRAMIENTAS" -["ADVANCED"]="AVANZADO" -["Quick Update — current branch"]="Actualización rápida — rama actual" -["Full Update (choose branch)"]="Actualización completa (elegir rama)" -["Emulator Only"]="Solo Emulador" -["Octane Client Only"]="Solo Cliente Octane" -["Renderer Only"]="Solo Renderizador" -["Configs Only"]="Solo Configuraciones" -["Dev Mode (skip builds, only configs)"]="Modo Dev (sin builds, solo configs)" -["Switch Branch"]="Cambiar Rama" -["Database"]="Base de Datos" -["Backup / Restore"]="Respaldo / Restaurar" -["Services"]="Servicios" -["Status"]="Estado" -["Health Check"]="Verificación de Salud" -["Clean"]="Limpiar" -["Logs"]="Registros" -["Config"]="Configuración" -["Git Log"]="Registro Git" -["Compare Branches"]="Comparar Ramas" -["Cron Scheduler"]="Programador Cron" -["View Git Stashes"]="Ver Stashes Git" -["Exit"]="Salir" -["Flyaway Repair"]="Reparación Flyaway" -["Auto-setup missing repos"]="Auto-instalar repos faltantes" -["Cleanup old Nitro dirs"]="Limpiar directorios Nitro antiguos" -["Rebuild Frontend"]="Reconstruir Frontend" -["Rebuild Emulator"]="Reconstruir Emulador" -["Select [0-24]"]="Seleccionar [0-24]" -["Fix Client Links (icons/furniture/gamedata)"]="Arreglar enlaces del Cliente (iconos/muebles/gamedata)" -["Press Enter to continue..."]="Presione Enter para continuar..." -["Goodbye!"]="¡Hasta luego!" -["Start update?"]="¿Iniciar actualización?" -["Continue?"]="¿Continuar?" -["Missing: %s"]="Falta: %s" -["Missing dir: %s"]="Directorio faltante: %s" -["All commands available"]="Todos los comandos disponibles" -["All directories exist"]="Todos los directorios existen" -["Database connected"]="Base de datos conectada" -["Maven build failed (see: %s)"]="Build Maven falló (ver: %s)" -["yarn install failed"]="yarn install falló" -["yarn build failed (see: %s)"]="yarn build falló (ver: %s)" -["Goodbye!"]="¡Hasta luego!" -["Return"]="Volver" -["none"]="ninguno" -["Yes"]="Sí" -["No"]="No" - ) ;; - de) _T=( -# ── MENU ────────────────────────────────────────────── -["UPDATE"]="AKTUALISIEREN" -["TOOLS"]="WERKZEUGE" -["ADVANCED"]="ERWEITERT" -["Quick Update — current branch"]="Schnellupdate — aktueller Branch" -["Full Update (choose branch)"]="Vollständiges Update (Branch wählen)" -["Emulator Only"]="Nur Emulator" -["Octane Client Only"]="Nur Octane Client" -["Renderer Only"]="Nur Renderer" -["Configs Only"]="Nur Konfigurationen" -["Dev Mode (skip builds, only configs)"]="Dev-Modus (ohne Builds, nur Konfigs)" -["Switch Branch"]="Branch wechseln" -["Database"]="Datenbank" -["Backup / Restore"]="Sicherung / Wiederherstellung" -["Services"]="Dienste" -["Status"]="Status" -["Health Check"]="Gesundheitsprüfung" -["Clean"]="Bereinigen" -["Logs"]="Protokolle" -["Config"]="Konfiguration" -["Git Log"]="Git-Verlauf" -["Compare Branches"]="Branches vergleichen" -["Cron Scheduler"]="Cron-Planer" -["View Git Stashes"]="Git-Stashes anzeigen" -["Exit"]="Beenden" -["Flyaway Repair"]="Flyaway-Reparatur" -["Auto-setup missing repos"]="Fehlende Repos automatisch einrichten" -["Cleanup old Nitro dirs"]="Alte Nitro-Verzeichnisse aufräumen" -["Rebuild Frontend"]="Frontend neu bauen" -["Rebuild Emulator"]="Emulator neu bauen" -["Select [0-24]"]="Auswählen [0-24]" -["Fix Client Links (icons/furniture/gamedata)"]="Client-Links reparieren (Icons/Möbel/Gamedata)" -["Press Enter to continue..."]="Drücken Sie Enter zum Fortfahren..." -["Goodbye!"]="Auf Wiedersehen!" -["Start update?"]="Update starten?" -["Continue?"]="Fortfahren?" -["Missing: %s"]="Fehlt: %s" -["Missing dir: %s"]="Verzeichnis fehlt: %s" -["All commands available"]="Alle Befehle verfügbar" -["All directories exist"]="Alle Verzeichnisse vorhanden" -["Database connected"]="Datenbank verbunden" -["Maven build failed (see: %s)"]="Maven-Build fehlgeschlagen (siehe: %s)" -["yarn install failed"]="yarn install fehlgeschlagen" -["yarn build failed (see: %s)"]="yarn build fehlgeschlagen (siehe: %s)" -["Goodbye!"]="Auf Wiedersehen!" -["Return"]="Zurück" -["none"]="keine" -["Yes"]="Ja" -["No"]="Nein" - ) ;; - it) _T=( -# ── MENU ────────────────────────────────────────────── -["UPDATE"]="AGGIORNA" -["TOOLS"]="STRUMENTI" -["ADVANCED"]="AVANZATE" -["Quick Update — current branch"]="Aggiornamento rapido — ramo attuale" -["Full Update (choose branch)"]="Aggiornamento completo (scegli ramo)" -["Emulator Only"]="Solo Emulatore" -["Octane Client Only"]="Solo Client Octane" -["Renderer Only"]="Solo Renderer" -["Configs Only"]="Solo Configurazioni" -["Dev Mode (skip builds, only configs)"]="Modalità Dev (senza build, solo configs)" -["Switch Branch"]="Cambia Ramo" -["Database"]="Database" -["Backup / Restore"]="Backup / Ripristino" -["Services"]="Servizi" -["Status"]="Stato" -["Health Check"]="Controllo Salute" -["Clean"]="Pulisci" -["Logs"]="Registri" -["Config"]="Configurazione" -["Git Log"]="Registro Git" -["Compare Branches"]="Confronta Rami" -["Cron Scheduler"]="Pianificatore Cron" -["View Git Stashes"]="Vedi Stash Git" -["Exit"]="Esci" -["Flyaway Repair"]="Riparazione Flyaway" -["Auto-setup missing repos"]="Auto-installazione repo mancanti" -["Cleanup old Nitro dirs"]="Pulisci vecchie cartelle Nitro" -["Rebuild Frontend"]="Ricostruisci Frontend" -["Rebuild Emulator"]="Ricostruisci Emulatore" -["Select [0-24]"]="Seleziona [0-24]" -["Fix Client Links (icons/furniture/gamedata)"]="Correggi link Client (icone/mobili/gamedata)" -["Press Enter to continue..."]="Premi Invio per continuare..." -["Goodbye!"]="Arrivederci!" -["Start update?"]="Avviare aggiornamento?" -["Continue?"]="Continuare?" -["Missing: %s"]="Mancante: %s" -["Missing dir: %s"]="Directory mancante: %s" -["All commands available"]="Tutti i comandi disponibili" -["All directories exist"]="Tutte le directory esistono" -["Database connected"]="Database connesso" -["Maven build failed (see: %s)"]="Build Maven fallita (vedi: %s)" -["yarn install failed"]="yarn install fallito" -["yarn build failed (see: %s)"]="yarn build fallito (vedi: %s)" -["Goodbye!"]="Arrivederci!" -["Return"]="Indietro" -["none"]="nessuno" -["Yes"]="Sì" -["No"]="No" - ) ;; - esac -} - -# ============================================================================= -# LOCKFILE -# ============================================================================= -acquire_lock() { - exec 9>"$LOCK_FILE" - if ! flock -n 9; then - die "Another instance is already running (lock: %s)" "$LOCK_FILE" - fi -} - -release_lock() { - # Parallel job subshells inherit fd 9; releasing it here would drop the - # main process's lock while the other jobs are still running. - [ -n "$PARALLEL_JOB" ] && return 0 - flock -u 9 2>/dev/null || true - rm -f "$LOCK_FILE" 2>/dev/null || true -} - -# ============================================================================= -# UI HELPERS -# ============================================================================= -log_write() { printf "[%s] [%s] %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" "$2" >> "$LOG_FILE" 2>/dev/null || true; } -info() { local m; m=$(_t "$1" "${@:2}"); echo -e " ${C_BLUE}${E_ARROW}${C_RESET} $m"; log_write "INFO" "$m"; } -ok() { local m; m=$(_t "$1" "${@:2}"); echo -e " ${C_GREEN}${E_OK}${C_RESET} $m"; log_write "OK" "$m"; } -warn() { local m; m=$(_t "$1" "${@:2}"); echo -e " ${C_YELLOW}${E_WARN}${C_RESET} $m"; WARNINGS=$((WARNINGS + 1)); log_write "WARN" "$m"; } -fail() { local m; m=$(_t "$1" "${@:2}"); echo -e " ${C_RED}${E_FAIL}${C_RESET} $m"; log_write "FAIL" "$m"; } - -die() { - local m; m=$(_t "$1" "${@:2}") - echo "" - echo -e " ${C_BG_RED}${C_WHITE}${C_BOLD} $(_t "ERROR") ${C_RESET} ${C_RED}$m${C_RESET}" - log_write "FATAL" "$m" - cleanup_notify_failure "$m" - release_lock - cursor_show - exit 1 -} -cursor_hide() { tput civis 2>/dev/null || true; } -cursor_show() { tput cnorm 2>/dev/null || true; } -clear_line() { printf "\r\033[K"; } -repeat() { local ch="${1:-=}" n="${2:-1}"; printf '%*s' "$n" '' | tr ' ' "$ch"; } -list_sorted() { local d="$1" p="$2" m="${3:-20}"; find "$d" -name "$p" -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -n "$m" | sed 's/^[0-9.]* //'; } - -service_active() { - local svc="$1" - if command -v systemctl &>/dev/null; then - systemctl is-active "$svc" &>/dev/null - elif command -v service &>/dev/null; then - service "$svc" status &>/dev/null - else - pgrep -x "$svc" &>/dev/null - fi -} - -# Redis protocol server name: classic redis-server or Dragonfly (drop-in -# replacement that also speaks the redis protocol, e.g. in production hotels). -redis_backend() { - if service_active redis-server; then echo redis-server - elif service_active dragonfly; then echo dragonfly - else echo redis-server; fi -} - -redis_active() { - service_active redis-server || service_active dragonfly -} - -format_size() { - local bytes="${1:-0}" - if command -v numfmt &>/dev/null; then - numfmt --to=iec "$bytes" 2>/dev/null || echo "${bytes}B" - else - echo "${bytes}B" - fi -} - -SPINNER_PID="" -spinner_start() { - local msg="${1:-Working...}" - local frames=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏") - cursor_hide - ( - local i=0 - while true; do - printf "\r ${C_CYAN}${frames[$i]}${C_RESET} %s " "$(_t "$msg")" - i=$(( (i + 1) % ${#frames[@]} )) - sleep 0.1 - done - ) & - SPINNER_PID=$! -} -spinner_stop() { - local st="${1:-ok}" - if [ -n "$SPINNER_PID" ]; then - kill "$SPINNER_PID" 2>/dev/null || true - wait "$SPINNER_PID" 2>/dev/null || true - SPINNER_PID="" - fi - clear_line; cursor_show - case "$st" in ok) printf "\r ${C_GREEN}${E_OK}${C_RESET} Done\n" ;; warn) printf "\r ${C_YELLOW}${E_WARN}${C_RESET} Done\n" ;; fail) printf "\r ${C_RED}${E_FAIL}${C_RESET} Failed\n" ;; esac -} - -step() { - local num="${1}" total="${2}" title="${3}" - echo "" - echo -e " ${C_BG_CYAN}${C_WHITE}${C_BOLD} Step ${num}/${total} — $(_t "$title") ${C_RESET}" - echo "" -} - -confirm() { - local msg="${1:-Continue?}" default="${2:-y}" - local hint="[Y/n]"; [ "$default" = "n" ] && hint="[y/N]" - echo -en " ${C_YELLOW}${E_ARROW}${C_RESET} ${C_BOLD}$(_t "$msg")${C_RESET} $hint " - local reply; read -rt 30 reply; reply="${reply:-$default}" - [[ "$reply" =~ ^[Yy] ]] -} - -dry_run() { - if [ "$DRY_RUN" = true ]; then - echo -e " ${C_DIM}[DRY-RUN]${C_RESET} $*" - return 0 - fi - return 1 -} - -box_top() { - local title="${1:-}" width="${2:-60}" - if [ -n "$title" ]; then - local tt; tt=$(_t "$title") - local pad=$((width - ${#tt} - 6)); [ $pad -lt 0 ] && pad=0 - printf "${C_CYAN}╔═══ ${C_BOLD}%s${C_RESET}${C_CYAN}" "$tt" - repeat '═' "$pad" - echo -e "╗${C_RESET}" - else - printf "${C_CYAN}╔"; repeat '═' "$width"; echo -e "╗${C_RESET}" - fi -} -box_kv() { - local key="${1}" val="${2}" width="${3:-60}" - local kk; kk=$(_t "$key") - local ck=$(echo -e "$kk" | sed 's/\x1b\[[0-9;]*m//g') - local cv=$(echo -e "$val" | sed 's/\x1b\[[0-9;]*m//g') - local pad=$((width - ${#ck} - ${#cv} - 6)); [ $pad -lt 0 ] && pad=0 - printf "${C_CYAN}║${C_RESET} %s%*s%s ${C_CYAN}║${C_RESET}\n" "$kk" $pad "" "$val" -} -box_line() { - local text="${1:-}" width="${2:-60}" - local ct=$(echo -e "$text" | sed 's/\x1b\[[0-9;]*m//g') - local pad=$((width - ${#ct} - 4)); [ $pad -lt 0 ] && pad=0 - printf "${C_CYAN}║${C_RESET} %s%*s${C_CYAN}║${C_RESET}\n" "$text" $pad "" -} -box_bottom() { local w="${1:-60}"; printf "${C_CYAN}╚"; repeat '═' "$w"; echo -e "╝${C_RESET}"; } - -# ============================================================================= -# NOTIFICATIONS -# ============================================================================= -notify() { - local status="$1" message="$2" - [ -z "$NOTIFY_URL" ] && return 0 - local elapsed=$(($(date +%s) - START_TIME)) - local ef; ef=$(printf '%dm %ds' $((elapsed / 60)) $((elapsed % 60))) - N_STATUS="$status" N_MESSAGE="$message" N_DURATION="$ef" \ - python3 -c " -import json, os -payload = { - 'text': '[Octane Update] ' + os.environ['N_STATUS'], - 'attachments': [{ - 'color': 'good' if os.environ['N_STATUS'] == 'SUCCESS' else 'danger', - 'fields': [ - {'title': 'Status', 'value': os.environ['N_MESSAGE'], 'short': True}, - {'title': 'Duration', 'value': os.environ['N_DURATION'], 'short': True} - ] - }] -} -print(json.dumps(payload)) -" | curl -s -o /dev/null -X POST "$NOTIFY_URL" -H "Content-Type: application/json" -d @- 2>/dev/null || true -} -cleanup_notify_failure() { $NOTIFY_FAILED && return; NOTIFY_FAILED=true; notify "FAILED" "$1"; } - -cleanup_on_exit() { - local ec=$? - # Parallel job subshells must never run the full cleanup (rollback, - # lock release, notify); only the main process owns those. Their status - # is captured by parallel_wait instead. - if [ -n "$PARALLEL_JOB" ]; then return $ec; fi - cursor_show; release_lock - if [ $ec -ne 0 ] && [ "$ROLLBACK_NEEDED" = true ]; then - echo -e "\n ${C_BG_RED}${C_WHITE}${C_BOLD} $(_t "ROLLBACK") ${C_RESET}" - echo -e " $(_t "Restoring config back-ups from") $CONFIG_BACKUP_DIR/rollback..." - if [ -d "$CONFIG_BACKUP_DIR/rollback" ]; then - cp -r "$CONFIG_BACKUP_DIR/rollback/"* "$NITRO_SRC_DIR/" 2>/dev/null || true - ok "Config files restored from rollback" - fi - restore_binaries - fi - # Preserve the real exit status: the last command in an EXIT trap defines - # the script's exit code, so never let a guard/notify call overwrite it. - if [ $ec -ne 0 ]; then - cleanup_notify_failure "Exit code $ec" - return $ec - fi - return 0 -} -trap cleanup_on_exit EXIT -trap 'release_lock; cursor_show; exit 1' SIGINT SIGTERM - -# ============================================================================= -# GIT HELPERS -# ============================================================================= -detect_branches() { - local repos=("$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER") - for repo in "${repos[@]}"; do - [ -d "$repo/.git" ] || continue - ( - cd "$repo" 2>/dev/null || exit - git branch -r 2>/dev/null | grep -v '\->' | sed 's/^[[:space:]]*//; s/^origin\///' | grep -v '^HEAD$' - ) - done | sort -u -} - -load_branches() { - local -n _arr="$1" - readarray -t _arr < <(detect_branches) -} - -detect_best_branch() { - local repo="$1" preferred="$2" - ( - cd "$repo" 2>/dev/null || { echo "main"; exit 0; } - local cur; cur=$(git branch --show-current 2>/dev/null || echo "") - for v in "$preferred" "${preferred^}" "$cur" "main" "master"; do - [ -n "$v" ] && git rev-parse --verify "$v" &>/dev/null && { echo "$v"; exit 0; } - done - echo "${cur:-main}" - ) -} - -git_update() { - local repo="$1" branch="$2" - local name; name=$(basename "$repo") - local rc=1 - ( - cd "$repo" || { warn "Cannot access $repo"; exit 2; } - # Only stash TRACKED changes (never untracked files — runtime config - # data in public/configuration/ must stay in place) and only when the - # working tree is dirty, to keep the pull fast-forwardable. The stash is - # left in the repo (recoverable via `git stash list`), never dropped. - if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then - git stash push -m "auto-update $(date +%F-%T)" 2>/dev/null \ - && warn "Local tracked changes in $(basename "$repo") stashed (see 'git stash list')" - fi - local old_head; old_head=$(git rev-parse HEAD 2>/dev/null || echo "") - local rb; rb=$(detect_best_branch "$repo" "$branch") - [ "$rb" != "$branch" ] && info "Branch '$branch' not in $(basename "$repo"), using '$rb'" - git checkout "$rb" 2>/dev/null || { warn "Cannot checkout '$rb' in $(basename "$repo")"; exit 2; } - local pulled=1 - for attempt in 1 2 3; do - if git pull --ff-only origin "$rb" 2>/dev/null; then - pulled=0; break - elif git pull --ff-only 2>/dev/null; then - pulled=0; break - fi - warn "Pull attempt %s failed in %s, retrying..." "$attempt" "$(basename "$repo")" >&2 - sleep 2 - done - if [ "$pulled" -ne 0 ]; then - warn "Pull failed in $(basename "$repo")" - echo "git error: pull failed" > "$PARALLEL_STATE_DIR/giterror-$name" - exit 2 - fi - # Remember the pre-update commit so a later build failure can reset the - # repo back to it; otherwise the next run would see HEAD==origin and - # silently skip the never-built change. - echo "$old_head" > "$PARALLEL_STATE_DIR/oldhead-$name" - [ "$(git rev-parse HEAD 2>/dev/null)" != "$old_head" ] && exit 0 - exit 1 - ) - rc=$? - # Return 0 = new commits fetched, 1 = nothing to do, 2 = real git error. - return $rc -} - -# Reverse a successful pull after a failed build, so the change is picked up -# again on the next run instead of being silently skipped (HEAD would otherwise -# equal origin and git_update would report "already up to date"). -git_revert_repo() { - local repo="$1" - local name; name=$(basename "$repo") - [ -f "$PARALLEL_STATE_DIR/oldhead-$name" ] || return 0 - local prev; prev=$(cat "$PARALLEL_STATE_DIR/oldhead-$name" 2>/dev/null) - [ -n "$prev" ] || return 0 - if git -C "$repo" reset --hard "$prev" >/dev/null 2>&1; then - warn "Reverted $(basename "$repo") to $prev (build failed)" - else - warn "Could not revert $(basename "$repo") to $prev" - fi - rm -f "$PARALLEL_STATE_DIR/oldhead-$name" 2>/dev/null || true -} - -# Run a yarn command inside a repo that pins an unavailable Yarn Berry version. -# Upstream package.json now declares e.g. "packageManager": "yarn@4.18.0", -# which the system Yarn 1 refuses to run ("current global version is 1.22.22") -# and which corepack cannot fetch (yarn 4 is not published on the registry). -# Drop the field for the duration of the command, then restore the pristine -# file so the repo stays clean for the next git pull. Runs inside the repo and -# always returns the caller to SCRIPT_DIR so cwd never leaks between repos. -yarn_run() { - local repo="$1"; shift - cd "$repo" 2>/dev/null || { warn "Cannot cd into %s" "$repo"; return 1; } - local modified=false - if python3 -c " -import json, sys -p = 'package.json' -d = json.load(open(p)) -pm = d.get('packageManager', '') -if pm.startswith('yarn@') and not pm.startswith('yarn@1.'): - d.pop('packageManager', None) - json.dump(d, open(p, 'w'), indent=2) - open(p, 'a').write(chr(10)) - sys.exit(0) -sys.exit(1) -" 2>/dev/null; then - modified=true - fi - yarn "$@" - local rc=$? - if [ "$modified" = true ]; then - git checkout -- package.json 2>/dev/null || true - fi - cd "$SCRIPT_DIR" 2>/dev/null || true - return $rc -} - -# yarn install scoped to a per-repo cache folder. Parallel installs (renderer + -# client) must never share a cache — yarn 1 corrupts the shared cache, leaving -# node_modules incomplete (vite/pixi.js missing) while still exiting 0. -# $1 = repo dir; remaining args forwarded to yarn. -yarn_install_repo() { - local repo="$1"; shift - local cache_dir="$YARN_CACHE_DIR/$(basename "$repo")" - mkdir -p "$cache_dir" 2>/dev/null || true - # NODE_ENV may be exported as "production" via .env (the CMS runs in prod), - # which makes yarn 1 skip devDependencies — vite is a devDep, so builds - # would fail with "vite: not found". Force a non-production env so the full - # dependency tree (including dev deps) is always installed. - NODE_ENV=development yarn_run "$repo" install --cache-folder "$cache_dir" "$@" >> "$LOG_FILE" 2>&1 -} - -# Nuke a repo's own yarn cache and node_modules for a truly clean reinstall. -# Parallel installs may have corrupted the cache, so it must be discarded too. -yarn_reset_repo() { - local repo="$1" - rm -rf "$repo/node_modules" "$YARN_CACHE_DIR/$(basename "$repo")" 2>/dev/null || true - mkdir -p "$YARN_CACHE_DIR/$(basename "$repo")" 2>/dev/null || true -} - -# ============================================================================= -# PARALLEL HELPERS -# ============================================================================= -PARALLEL_PIDS=() - -PARALLEL_NAMES=() -PARALLEL_FAILED=() -PARALLEL_RESULTS=() - -# Each parallel job runs in a subshell, so variable changes (HAD_UPDATES, -# UPDATED_REPOS, NITRO_BUILT) made inside update_renderer/update_client would -# otherwise be lost. Jobs persist their state to a per-job file which -# parallel_wait re-reads back into the parent shell. -PARALLEL_STATE_DIR="${TMPDIR:-/tmp}/nitro-parallel-$$" -mkdir -p "$PARALLEL_STATE_DIR" 2>/dev/null || true - -parallel_run() { - local name="$1"; shift - PARALLEL_NAMES+=("$name") - ( - # This subshell inherits the parent's EXIT trap and lock fd 9. Mark it - # so cleanup_on_exit/release_lock become no-ops here: the main process - # owns rollback, lock release and notifications. - PARALLEL_JOB=1 - # Snapshot inherited state so we only persist the delta back: the - # emulator (run before the parallel phase) may already be in - # UPDATED_REPOS, so emitting the full array would duplicate it. - local _orig="${UPDATED_REPOS[*]:-}" - "$@" >/dev/null 2>&1 - local rc=$? - { - # Note: exit status is captured via `wait`; only fold state vars back. - echo "HAD_UPDATES=$HAD_UPDATES" - echo "NITRO_BUILT=$NITRO_BUILT" - for r in "${UPDATED_REPOS[@]:-}"; do - case " $_orig " in - *" $r "*) ;; - *) printf 'UPDATED_REPOS+=(\047%s\047)\n' "$r" ;; - esac - done - } > "$PARALLEL_STATE_DIR/$name" - exit $rc - ) & - PARALLEL_PIDS+=($!) -} - -parallel_wait() { - # Collect per-job pass/fail based on the real exit status of each job. - PARALLEL_RESULTS=() - PARALLEL_FAILED=() - local i=0 - for pid in "${PARALLEL_PIDS[@]}"; do - local name="${PARALLEL_NAMES[$i]}" - if wait "$pid" 2>/dev/null; then - PARALLEL_RESULTS+=("__PARALLEL_OK__${name}") - else - PARALLEL_RESULTS+=("__PARALLEL_FAIL__${name}") - PARALLEL_FAILED+=("$name") - fi - # Fold the job's persisted state back into the parent shell so the - # summary reflects renderer/client updates, not just the emulator. - if [ -f "$PARALLEL_STATE_DIR/$name" ]; then - . "$PARALLEL_STATE_DIR/$name" - rm -f "$PARALLEL_STATE_DIR/$name" - fi - i=$((i+1)) - done - PARALLEL_PIDS=() - PARALLEL_NAMES=() - rm -rf "$PARALLEL_STATE_DIR" 2>/dev/null || true -} - -parallel_failed() { - # Returns 0 (success) when no backgrounded job failed. - [ ${#PARALLEL_FAILED[@]} -eq 0 ] -} - -# ============================================================================= -# CONFIG BACKUP & VALIDATION -# ============================================================================= -backup_configs() { - local ts=$(date +%Y%m%d-%H%M%S) - local bdir="$CONFIG_BACKUP_DIR/$ts" - mkdir -p "$bdir" - if [ -d "$NITRO_SRC_DIR" ]; then - cp -r "$NITRO_SRC_DIR"/*.json "$bdir/" 2>/dev/null || true - fi - # Keep a rolling 'rollback' pointer - rm -rf "$CONFIG_BACKUP_DIR/rollback" - cp -r "$bdir" "$CONFIG_BACKUP_DIR/rollback" 2>/dev/null || true - echo "$bdir" -} - -validate_json() { - local file="$1" - # Strict JSON first... - if python3 -m json.tool "$file" >/dev/null 2>&1; then - return 0 - fi - # ...otherwise fall back to JSONC (the renderer configs legitimately carry - # // and /* */ comments — that is not an error, it is by design). - if command -v node &>/dev/null && \ - node -e "require('jsonc-parser').parse(require('fs').readFileSync(process.argv[1],'utf-8'),[],{allowTrailingComma:true,allowEmptyContent:true});" "$file" >/dev/null 2>&1; then - return 0 - fi - return 1 -} - -validate_configs() { - local dir="$1" - local errors=0 - for f in "$dir"/*.json; do - [ -f "$f" ] || continue - case "$(basename "$f")" in - *jsonc*|*.example*) continue ;; - esac - if ! validate_json "$f"; then - warn "Invalid JSON: %s" "$(basename "$f")" - errors=$((errors + 1)) - fi - done - [ "$errors" -eq 0 ] && ok "All config files valid" || warn "$errors config(s) invalid" -} - -# ============================================================================= -# ENV FILE SYNC & VALIDATION -# ============================================================================= -# Merge any keys documented in /.env.example but missing from the live -# /.env so new options introduced upstream are filled in with their -# default values on every update. Existing values are ALWAYS preserved. -sync_env_file() { - local base_dir="${1:-$SCRIPT_DIR}" - local ex="$base_dir/.env.example" en="$base_dir/.env" - [ -f "$ex" ] || { info "No .env.example in %s — skipping env sync" "$(basename "$base_dir")"; return 0; } - local tmp; tmp=$(mktemp) - env_example="$ex" env_file="$en" env_added="$tmp" python3 -c ' -import os, re -ex, en, out = os.environ["env_example"], os.environ["env_file"], os.environ["env_added"] -seen = set() -if os.path.exists(en): - with open(en, encoding="utf-8", errors="replace") as f: - for line in f: - m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=", line) - if m: - seen.add(m.group(1)) -additions = [] -with open(ex, encoding="utf-8", errors="replace") as f: - for line in f: - m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$", line) - if not m: - continue - key = m.group(1) - if key in seen: - continue - seen.add(key) - value = m.group(2).strip() - if value and not (value[0] in "\"\x27" or value.startswith("${")): - value = "\"" + value + "\"" - additions.append(f"{key}={value}\n") -with open(out, "w", encoding="utf-8") as f: - f.writelines(additions) -' 2>/dev/null || true - if [ -s "$tmp" ]; then - if { - printf "\n# --- Auto-added by %s on %s ---\n" "$SCRIPT_NAME" "$(date +%F)" - cat "$tmp" - } >> "$en" 2>/dev/null; then - # Re-load new values so the rest of the run sees them. - set -a; . "$en"; set +a - ok "%s new variable(s) added to %s" "$(wc -l < "$tmp")" "$(basename "$en")" - else - warn "Cannot update %s (no write permission) — add the %s variable(s) manually" \ - "$(basename "$en")" "$(wc -l < "$tmp")" - fi - else - ok "Environment %s is up to date" "$(basename "$en")" - fi - rm -f "$tmp" 2>/dev/null || true -} - -# Sanity-check a .env file for the failure modes that actually break a deploy: -# duplicate keys, and a value concatenated onto another key (missing newline), -# e.g. BYPARR_URL=http://localhost:8191AUTH_TRUST_HOST="..." -validate_env_file() { - local base_dir="${1:-$SCRIPT_DIR}" - local en="$base_dir/.env" - [ -f "$en" ] || { warn "No .env file in %s" "$(basename "$base_dir")"; return 1; } - local issues; issues=$(python3 - "$en" <<'PY' -import sys, re -path = sys.argv[1] -out = [] -with open(path, encoding="utf-8", errors="replace") as f: - for i, line in enumerate(f, 1): - s = line.strip() - if not s or s.startswith("#") or s.startswith("export "): - continue - m = re.match(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$", s) - if not m: - out.append(f"line {i}: unparseable {s[:60]}") - continue - val = m.group(2) - # URL immediately followed by an uppercase KEY= (missing newline). - if re.search(r"https?://[^\s\"']+?[A-Z][A-Z0-9_]{2,40}\s*=", val): - out.append(f"line {i}: concatenated value (missing newline) {s[:90]}") -PY -) - if [ -n "$issues" ]; then - while IFS= read -r line; do warn "%s" "$line"; done <<< "$issues" - fail "Invalid .env — fix before next startup" - return 1 - fi - local dup; dup=$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*=' "$en" 2>/dev/null | sort | uniq -d | tr '\n' ' ') - if [ -n "$dup" ]; then - warn "Duplicate key(s) in %s: %s" "$(basename "$en")" "$dup" - fi - # Missing critical vars commonly needed for a healthy boot. - local req=(DATABASE_URL REDIS_URL PORT HOTEL_NAME) - local miss=() - for r in "${req[@]}"; do - grep -qE "^[[:space:]]*${r}[[:space:]]*=" "$en" || miss+=("$r") - done - [ ${#miss[@]} -gt 0 ] && warn "Missing variable(s) in %s: %s" "$(basename "$en")" "${miss[*]}" - return 0 -} - -# ============================================================================= -# BINARY BACKUP & ROLLBACK -# ============================================================================= -backup_binaries() { - [ -n "$BINARY_BACKUP_DIR" ] && return - BINARY_BACKUP_DIR="$CONFIG_BACKUP_DIR/binaries-$(date +%Y%m%d-%H%M%S)" - mkdir -p "$BINARY_BACKUP_DIR" - - local jar_dir="$EMULATOR_MODULE/target" - if [ -d "$jar_dir" ] && ls "$jar_dir"/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then - cp "$jar_dir"/Polaris-*-jar-with-dependencies.jar "$BINARY_BACKUP_DIR/" 2>/dev/null || true - ok "Emulator JAR backed up" - fi - - local client_dist="$NITRO_CLIENT/dist" - if [ -d "$client_dist" ]; then - cp -r "$client_dist" "$BINARY_BACKUP_DIR/client-dist" 2>/dev/null || true - ok "Octane dist backed up" - fi -} - -restore_binaries() { - [ -z "$BINARY_BACKUP_DIR" ] && return - [ ! -d "$BINARY_BACKUP_DIR" ] && return - echo "" - echo -e " ${C_BG_RED}${C_WHITE}${C_BOLD} $(_t "RESTORE BINARIES") ${C_RESET}" - - if ls "$BINARY_BACKUP_DIR"/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then - local jar_dir="$EMULATOR_MODULE/target" - mkdir -p "$jar_dir" - cp "$BINARY_BACKUP_DIR"/Polaris-*-jar-with-dependencies.jar "$jar_dir/" 2>/dev/null || true - ok "Emulator JAR restored" - fi - - if [ -d "$BINARY_BACKUP_DIR/client-dist" ]; then - local client_dist="$NITRO_CLIENT/dist" - rm -rf "$client_dist" 2>/dev/null || true - cp -r "$BINARY_BACKUP_DIR/client-dist" "$client_dist" 2>/dev/null || true - ok "Octane dist restored" - fi -} - -# ============================================================================= -# OLD NITRO CLEANUP -# ============================================================================= -cmd_cleanup_old_nitro() { - step 1 1 "Cleanup Old Nitro" - echo "" - - local www_dir="/var/www" - local old_dirs=() - local current_client="$NITRO_CLIENT" - local current_renderer="$NITRO_RENDERER" - - # Scan for old Nitro directories that are NOT the current active ones - for candidate in "$www_dir"/Nitro-V3 "$www_dir"/Nitro_Render_V3 "$www_dir"/Nitro_Render "$www_dir"/Nitro "$www_dir"/Nitro-V3-*; do - [ -d "$candidate" ] || continue - [ "$(realpath "$candidate" 2>/dev/null)" = "$(realpath "$current_client" 2>/dev/null)" ] && continue - [ "$(realpath "$candidate" 2>/dev/null)" = "$(realpath "$current_renderer" 2>/dev/null)" ] && continue - old_dirs+=("$candidate") - done - - if [ ${#old_dirs[@]} -eq 0 ]; then - ok "No old Nitro directories found" - else - echo -e " ${C_YELLOW}${C_BOLD}Found ${#old_dirs[@]} old Nitro director(y/ies):${C_RESET}" - echo "" - local total_size=0 - for d in "${old_dirs[@]}"; do - local sz=$(du -sh "$d" 2>/dev/null | cut -f1) - local files=$(find "$d" -type f 2>/dev/null | wc -l) - echo -e " ${C_RED}✗${C_RESET} ${C_BOLD}$(basename "$d")${C_RESET} ${C_DIM}($sz, $files files)${C_RESET}" - local bytes=$(du -sb "$d" 2>/dev/null | cut -f1) - total_size=$((total_size + bytes)) - done - echo "" - echo -e " ${C_CYAN}Total space: $(numfmt --to=iec "$total_size" 2>/dev/null || echo "${total_size}B")${C_RESET}" - echo "" - - # ── STEP 1: Migrate symlinks pointing to old dirs ── - echo -e " ${C_BOLD}Checking symlinks...${C_RESET}" - local symlinks_fixed=0 - local root_cmd="" - [ "$(id -u)" -eq 0 ] || root_cmd="sudo -n" - - for old_dir in "${old_dirs[@]}"; do - local old_name=$(basename "$old_dir") - # Determine the correct new target - local new_target="" - case "$old_name" in - Nitro-V3|Nitro-V3-*) new_target="$current_client" ;; - Nitro_Render_V3|Nitro_Render) new_target="$current_renderer" ;; - esac - [ -z "$new_target" ] && continue - - # Find all symlinks on the system pointing to the old dir - while IFS= read -r link; do - [ -z "$link" ] && continue - local link_target=$(readlink -f "$link" 2>/dev/null) - case "$link_target" in - *"$old_name"*|*"$old_dir"*) - echo -e " ${C_GREEN}${E_ARROW}${C_RESET} $link → $new_target" - $root_cmd ln -sfn "$new_target" "$link" 2>/dev/null && symlinks_fixed=$((symlinks_fixed + 1)) || warn "Could not update symlink: %s" "$link" - ;; - esac - done < <(find /var/www /etc/nginx /etc/systemd /opt -maxdepth 4 -type l 2>/dev/null) - done - [ "$symlinks_fixed" -gt 0 ] && ok "Updated %s symlink(s)" "$symlinks_fixed" || ok "No symlinks to update" - - # ── STEP 2: Update nginx configs referencing old paths ── - echo "" - echo -e " ${C_BOLD}Checking nginx configs...${C_RESET}" - local nginx_fixed=0 - for conf in /etc/nginx/sites-enabled/*.conf /etc/nginx/sites-available/*.conf /etc/nginx/conf.d/*.conf; do - [ -f "$conf" ] || continue - local needs_update=false - for old_dir in "${old_dirs[@]}"; do - grep -q "$old_dir" "$conf" 2>/dev/null && needs_update=true && break - done - if [ "$needs_update" = true ]; then - local bak="$conf.bak-nitro-cleanup-$(date +%s)" - $root_cmd cp "$conf" "$bak" 2>/dev/null || true - for old_dir in "${old_dirs[@]}"; do - local old_name=$(basename "$old_dir") - local new_target="" - case "$old_name" in - Nitro-V3|Nitro-V3-*) new_target="$current_client" ;; - Nitro_Render_V3|Nitro_Render) new_target="$current_renderer" ;; - esac - [ -z "$new_target" ] && continue - $root_cmd sed -i "s|$old_dir|$new_target|g" "$conf" 2>/dev/null && nginx_fixed=$((nginx_fixed + 1)) - done - echo -e " ${C_GREEN}${E_ARROW}${C_RESET} Updated: $(basename "$conf")" - fi - done - [ "$nginx_fixed" -gt 0 ] && ok "Updated %s nginx reference(s) (backups created)" "$nginx_fixed" || ok "No nginx configs to update" - - # ── STEP 3: Update systemd service files referencing old paths ── - echo "" - echo -e " ${C_BOLD}Checking systemd services...${C_RESET}" - local svc_fixed=0 - for svc_file in /etc/systemd/system/*.service /etc/systemd/system/emulator.service.d/*.conf; do - [ -f "$svc_file" ] || continue - local needs_update=false - for old_dir in "${old_dirs[@]}"; do - grep -q "$old_dir" "$svc_file" 2>/dev/null && needs_update=true && break - done - if [ "$needs_update" = true ]; then - local bak="$svc_file.bak-nitro-cleanup-$(date +%s)" - $root_cmd cp "$svc_file" "$bak" 2>/dev/null || true - for old_dir in "${old_dirs[@]}"; do - local old_name=$(basename "$old_dir") - local new_target="" - case "$old_name" in - Nitro-V3|Nitro-V3-*) new_target="$current_client" ;; - Nitro_Render_V3|Nitro_Render) new_target="$current_renderer" ;; - esac - [ -z "$new_target" ] && continue - $root_cmd sed -i "s|$old_dir|$new_target|g" "$svc_file" 2>/dev/null && svc_fixed=$((svc_fixed + 1)) - done - echo -e " ${C_GREEN}${E_ARROW}${C_RESET} Updated: $(basename "$svc_file")" - fi - done - [ "$svc_fixed" -gt 0 ] && $root_cmd systemctl daemon-reload 2>/dev/null - [ "$svc_fixed" -gt 0 ] && ok "Updated %s service reference(s)" "$svc_fixed" || ok "No systemd services to update" - - # ── STEP 4: Check for any other file references ── - echo "" - echo -e " ${C_BOLD}Checking other file references...${C_RESET}" - local other_refs=0 - for old_dir in "${old_dirs[@]}"; do - local old_name=$(basename "$old_dir") - local refs=$(grep -rl "$old_name" /var/www/atom-nexst/*.sh /var/www/atom-nexst/.env 2>/dev/null | grep -v "update-Nitrov3.sh" | wc -l) - other_refs=$((other_refs + refs)) - if [ "$refs" -gt 0 ]; then - echo -e " ${C_YELLOW}${E_WARN}${C_RESET} $refs file(s) still reference ${C_BOLD}$old_name${C_RESET}" - fi - done - [ "$other_refs" -gt 0 ] && warn "Some files still reference old names — check manually" || ok "No other references found" - - # ── STEP 5: Remove old directories ── - echo "" - echo -e " ${C_BOLD}Summary:${C_RESET}" - echo -e " Symlinks updated: ${C_GREEN}$symlinks_fixed${C_RESET}" - echo -e " Nginx refs fixed: ${C_GREEN}$nginx_fixed${C_RESET}" - echo -e " Service refs fixed: ${C_GREEN}$svc_fixed${C_RESET}" - echo -e " Other refs: ${C_YELLOW}$other_refs${C_RESET}" - echo "" - - if confirm "Now remove the old directories?"; then - for d in "${old_dirs[@]}"; do - local name=$(basename "$d") - spinner_start "Removing %s..." "$name" - if rm -rf "$d" 2>/dev/null; then - spinner_stop ok - ok "Removed %s" "$name" - else - spinner_stop fail - warn "Failed to remove %s" "$name" - fi - done - ok "Old Nitro cleanup completed" - else - info "Skipped — old directories kept" - fi - fi - - # Also clean up old /tmp nitro files - local tmp_count=$(find /tmp -maxdepth 1 -name 'nitro-*' -o -name 'epicnext-*' 2>/dev/null | wc -l) - if [ "$tmp_count" -gt 0 ]; then - echo "" - echo -e " ${C_YELLOW}Found $tmp_count old temp file(s) in /tmp${C_RESET}" - if confirm "Clean /tmp nitro/epicnext files?"; then - rm -rf /tmp/nitro-* /tmp/epicnext-* 2>/dev/null || true - ok "Temp files cleaned" - fi - fi - - # Clean up old log files (older than 30 days) - local old_logs=0 - if [ -d "$LOG_DIR" ]; then - old_logs=$(find "$LOG_DIR" -name 'update-*.log' -mtime +30 2>/dev/null | wc -l) - if [ "$old_logs" -gt 0 ]; then - echo "" - echo -e " ${C_YELLOW}Found $old_logs old log file(s) (>30 days)${C_RESET}" - if confirm "Clean old logs?"; then - find "$LOG_DIR" -name 'update-*.log' -mtime +30 -exec rm -f {} \; 2>/dev/null || true - ok "Old logs cleaned" - fi - fi - fi - - # Clean up old config backups (keep last 5) - if [ -d "$CONFIG_BACKUP_DIR" ]; then - local backup_count=$(find "$CONFIG_BACKUP_DIR" -maxdepth 1 -type d ! -name 'rollback' ! -name 'config-backups' 2>/dev/null | wc -l) - if [ "$backup_count" -gt 5 ]; then - local to_remove=$((backup_count - 5)) - echo "" - echo -e " ${C_YELLOW}Found $backup_count config backups (keeping 5, removing $to_remove)${C_RESET}" - if confirm "Prune old config backups?"; then - find "$CONFIG_BACKUP_DIR" -maxdepth 1 -type d ! -name 'rollback' ! -name 'config-backups' -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +6 | sed 's/^[0-9.]* //' | xargs -r rm -rf || true - ok "Old config backups pruned" - fi - fi - fi - - # Clean up old SQL backups (keep last 5) - if [ -d "$BACKUP_DIR" ]; then - local sql_count=$(find "$BACKUP_DIR" -maxdepth 1 -name '*.sql' 2>/dev/null | wc -l) - if [ "$sql_count" -gt 5 ]; then - local to_remove=$((sql_count - 5)) - echo "" - echo -e " ${C_YELLOW}Found $sql_count SQL backups (keeping 5, removing $to_remove)${C_RESET}" - if confirm "Prune old SQL backups?"; then - find "$BACKUP_DIR" -maxdepth 1 -name '*.sql' -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +6 | sed 's/^[0-9.]* //' | xargs -r rm -f || true - ok "Old SQL backups pruned" - fi - fi - fi - - echo "" - ok "Cleanup check completed" -} - -# ============================================================================= -# AUTO-SETUP (clone missing repos) -# ============================================================================= -auto_setup_missing_repos() { - local cloned=false - local needs_link=false - - if [ ! -d "$NITRO_CLIENT" ] || [ ! -d "$NITRO_CLIENT/.git" ]; then - echo "" - echo -e " ${C_YELLOW}${E_WARN}${C_RESET} ${C_BOLD}Octane client not found at $NITRO_CLIENT${C_RESET}" - info "Cloning Octane client from %s..." "$GIT_REPO_CLIENT" - spinner_start "Cloning Octane client..." - if git clone --depth 1 "$GIT_REPO_CLIENT" "$NITRO_CLIENT" >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Octane client cloned successfully" - cloned=true; needs_link=true - else - spinner_stop fail - die "Failed to clone Octane client from %s" "$GIT_REPO_CLIENT" - fi - fi - - if [ ! -d "$NITRO_RENDERER" ] || [ ! -d "$NITRO_RENDERER/.git" ]; then - echo "" - echo -e " ${C_YELLOW}${E_WARN}${C_RESET} ${C_BOLD}Octane renderer not found at $NITRO_RENDERER${C_RESET}" - info "Cloning Octane renderer from %s..." "$GIT_REPO_RENDERER" - spinner_start "Cloning Octane renderer..." - if git clone --depth 1 "$GIT_REPO_RENDERER" "$NITRO_RENDERER" >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Octane renderer cloned successfully" - cloned=true; needs_link=true - else - spinner_stop fail - die "Failed to clone Octane renderer from %s" "$GIT_REPO_RENDERER" - fi - fi - - # Also detect when either repo exists but was never linked - if [ "$needs_link" = false ]; then - if [ -d "$NITRO_RENDERER" ] && [ -d "$NITRO_RENDERER/.git" ]; then - # Check if yarn global link is registered — if not, we need to re-link - if [ ! -d "$(yarn global dir 2>/dev/null)/node_modules/@nitrots/nitro-renderer" ] 2>/dev/null && \ - [ ! -L "$(yarn global dir 2>/dev/null)/node_modules/@nitrots/nitro-renderer" ] 2>/dev/null; then - needs_link=true - fi - fi - fi - - if [ "$cloned" = true ] || [ "$needs_link" = true ]; then - echo "" - info "Installing dependencies..." - - # Step 1: Install renderer deps - if [ -d "$NITRO_RENDERER" ] && [ -f "$NITRO_RENDERER/package.json" ]; then - spinner_start "Installing renderer deps..." - yarn_install_repo "$NITRO_RENDERER" --prefer-offline >> "$LOG_FILE" 2>&1 || \ - yarn_install_repo "$NITRO_RENDERER" >> "$LOG_FILE" 2>&1 || warn "Renderer yarn install failed" - spinner_stop ok - ok "Renderer dependencies installed" - - # Step 2: Register renderer as a global yarn link - spinner_start "Linking renderer package..." - cd "$NITRO_RENDERER" - yarn link >> "$LOG_FILE" 2>&1 || warn "yarn link in renderer failed (may already be linked)" - cd "$SCRIPT_DIR" - spinner_stop ok - ok "Renderer linked globally" - fi - - # Step 3: Install client deps - if [ -d "$NITRO_CLIENT" ] && [ -f "$NITRO_CLIENT/package.json" ]; then - spinner_start "Installing client deps..." - yarn_install_repo "$NITRO_CLIENT" --prefer-offline >> "$LOG_FILE" 2>&1 || \ - yarn_install_repo "$NITRO_CLIENT" >> "$LOG_FILE" 2>&1 || warn "Client yarn install failed" - spinner_stop ok - ok "Client dependencies installed" - - # Step 4: Link renderer into client - spinner_start "Linking renderer to client..." - cd "$NITRO_CLIENT" - yarn link "@nitrots/nitro-renderer" >> "$LOG_FILE" 2>&1 || warn "yarn link to renderer failed (may already be linked)" - cd "$SCRIPT_DIR" - spinner_stop ok - ok "Renderer linked to client" - fi - - # Step 5: Setup config files from examples - echo "" - info "Setting up configuration files..." - _setup_config_files - - # Step 6: Create .nitro-build.json for JSONC mode - if [ ! -f "$NITRO_CLIENT/.nitro-build.json" ]; then - echo '{"jsonMode":"jsonc"}' > "$NITRO_CLIENT/.nitro-build.json" - ok "Created .nitro-build.json (jsonc mode)" - fi - - # Step 7: Build client frontend - if [ -d "$NITRO_CLIENT" ] && [ -f "$NITRO_CLIENT/package.json" ]; then - spinner_start "Building client frontend..." - if yarn_run "$NITRO_CLIENT" build >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Client frontend built" - else - spinner_stop fail - warn "Client build failed — run update to retry" - fi - fi - - mkdir -p "$NITRO_SRC_DIR" 2>/dev/null || true - mkdir -p "$NITRO_CLIENT/dist/custom-themes" 2>/dev/null || true - [ ! -f "$NITRO_CLIENT/dist/custom-themes/index.json" ] && echo '{"themes":[]}' > "$NITRO_CLIENT/dist/custom-themes/index.json" - fix_perms - echo "" - ok "Auto-setup completed — all repos ready" - else - ok "All repos already present" - fi -} - -# Copy example config files to their runtime names, handling renamed files -# in the Octane repo (renderer-config..jsonc.example.json etc.). Existing -# runtime files are MERGED via merge-config.cjs (new keys filled in, local -# values preserved) instead of being skipped — this is what keeps configs -# "gecompleteerd" when upstream adds new options. -_setup_config_files() { - local src_dir="$NITRO_SRC_DIR" - [ ! -d "$src_dir" ] && mkdir -p "$src_dir" 2>/dev/null - local copied=0 - local ms="$SCRIPT_DIR/scripts/merge-config.cjs" - local merge_or_copy - merge_or_copy() { - local ex="$1" tgt="$2" - if [ -f "$tgt" ]; then - node "$ms" "$ex" "$tgt" 2>/dev/null || true - else - cp "$ex" "$tgt" 2>/dev/null || return 1 - fi - return 0 - } - - # Standard *.example files (ui-config, client-mode, etc.) - for ef in "$src_dir"/*.example; do - [ -f "$ef" ] || continue - local b=$(basename "$ef"); local tn="${b%.example}" - case "$tn" in *.*) ;; *) tn="$tn.json" ;; esac - if merge_or_copy "$ef" "$src_dir/$tn"; then copied=$((copied + 1)); fi - done - - # Handle renamed renderer config files - # Repo has: renderer-config..jsonc.example.json / renderer-config.json.example.json - # Runtime needs: renderer-config.jsonc (JSONC) or renderer-config.json - local rc_jsonc="$src_dir/renderer-config..jsonc.example.json" - local rc_json="$src_dir/renderer-config.json.example.json" - if [ -f "$rc_jsonc" ]; then - if merge_or_copy "$rc_jsonc" "$src_dir/renderer-config.jsonc"; then copied=$((copied + 1)); fi - fi - if [ -f "$rc_json" ]; then - if merge_or_copy "$rc_json" "$src_dir/renderer-config.json"; then copied=$((copied + 1)); fi - fi - - # Merge/copy to gamedata config dir too - if [ -d "$GAMEDATA_CONF_DIR" ]; then - for f in "$src_dir"/*.json "$src_dir"/*.jsonc; do - [ -f "$f" ] || continue - local fn=$(basename "$f") - # Skip example/source files - case "$fn" in *.example*|*.example) continue ;; esac - merge_or_copy "$f" "$GAMEDATA_CONF_DIR/$fn" 2>/dev/null || true - done - fi - - # Copy to dist/configuration if it exists - local dcd="$NITRO_CLIENT/dist/configuration" - if [ -d "$dcd" ]; then - for f in "$src_dir"/*.json "$src_dir"/*.jsonc; do - [ -f "$f" ] || continue - local fn=$(basename "$f") - case "$fn" in *.example*|*.example) continue ;; esac - cp "$f" "$dcd/$fn" 2>/dev/null || true - done - fi - - ok "%s config file(s) copied/synced" "$copied" -} - -# ============================================================================= -# PRE-FLIGHT CHECKS -# ============================================================================= -preflight_check() { - local fatal=false - - for cmd in git mvn node python3 yarn; do - command -v "$cmd" &>/dev/null || { fail "Missing: $cmd"; fatal=true; } - done - command -v "$MYSQL_CLIENT" &>/dev/null || { fail "Missing: $MYSQL_CLIENT"; fatal=true; } - command -v "$MYSQL_DUMP" &>/dev/null || { fail "Missing: $MYSQL_DUMP"; fatal=true; } - $fatal && die "Required commands missing" - - ok "All commands available" - - # Keep .env complete: fill in any new variables documented in - # .env.example and validate the result before touching anything. - info "Environment sync & validation..." - sync_env_file "$SCRIPT_DIR" || true - validate_env_file "$SCRIPT_DIR" || fatal=true - $fatal && die "Invalid .env — fix before next startup" - - # Auto-clone missing repos instead of dying - local missing_repos=false - for d in "$NITRO_RENDERER" "$NITRO_CLIENT"; do - [ -d "$d" ] && [ -d "$d/.git" ] || missing_repos=true - done - if [ "$missing_repos" = true ]; then - info "Missing repos detected — running auto-setup..." - auto_setup_missing_repos - fi - - for d in "$EMULATOR_REPO" "$EMULATOR_MODULE" "$NITRO_RENDERER" "$NITRO_CLIENT" "$NITRO_SRC_DIR"; do - [ -d "$d" ] || die "Missing dir: $d" - done - ok "All directories exist" - - local total_kb=$(df --output=avail "$EMULATOR_DIR" 2>/dev/null | tail -1) - local total_gb=$((total_kb / 1024 / 1024)) - [ "$total_gb" -lt "$MIN_DISK_GB" ] && die "Only ${total_gb}GB free (min ${MIN_DISK_GB}GB)" - if [ "$total_gb" -lt $((MIN_DISK_GB * 2)) ]; then - warn "Low disk: %sGB free" "$total_gb" - else - ok "%sGB disk available" "$total_gb" - fi - - $MYSQL_CLIENT $MYSQL_CRED -e "SELECT 1" "$DB_NAME" &>/dev/null || die "DB connection failed" - ok "Database connected" -} - -# ============================================================================= -# UPDATE FUNCTIONS -# ============================================================================= -update_emulator() { - step 2 9 "Update & Build Emulator" - local grc=1 - if git_update "$EMULATOR_REPO" "$NITRO_BRANCH"; then - grc=0 - else - grc=$? - fi - if [ $grc -eq 2 ]; then - die "Emulator update failed (git error)" - elif [ $grc -eq 0 ]; then - HAD_UPDATES=true; UPDATED_REPOS+=("Emulator") - cd "$EMULATOR_MODULE" - backup_binaries - ROLLBACK_NEEDED=true - # The emulator reads its live config from target/config.ini (gitignored, - # so git pull never touches it). Guard it anyway: if the Maven build ever - # rebuilds target/ without it, the live config is restored verbatim so - # the DB/hotel settings survive every update untouched. - local emu_cfg="$EMULATOR_MODULE/target/config.ini" - local emu_cfg_bak="" - if [ -f "$emu_cfg" ]; then - emu_cfg_bak="$(mktemp -d)/config.ini" - cp "$emu_cfg" "$emu_cfg_bak" 2>/dev/null && info "Emulator config.ini backed up" \ - || { rm -rf "${emu_cfg_bak%/config.ini}" 2>/dev/null || true; emu_cfg_bak=""; } - fi - spinner_start "Building emulator (parallel)..." - # Prefer offline mode when the local Maven repo is warm; fall back to - # online if offline resolution fails. ccache speeds up repeated JNI/C++ - # native compiles if present. - command -v ccache >/dev/null 2>&1 && export CC="ccache gcc" CXX="ccache g++" - if mvn package -q -T 1C -o -Dmaven.test.skip=true >> "$LOG_FILE" 2>&1 || \ - mvn package -q -T 1C -Dmaven.test.skip=true >> "$LOG_FILE" 2>&1; then - spinner_stop ok - else - spinner_stop fail - tail -30 "$LOG_FILE" | grep -E '(ERROR|FAILURE|BUILD)' || true - git_revert_repo "$EMULATOR_REPO" - die "Maven build failed (see: %s)" "$LOG_FILE" - fi - # Restore the live config if the build produced a target/ without it. - if [ ! -f "$emu_cfg" ]; then - if [ -n "$emu_cfg_bak" ] && [ -f "$emu_cfg_bak" ]; then - mkdir -p "$EMULATOR_MODULE/target" - cp "$emu_cfg_bak" "$emu_cfg" 2>/dev/null && ok "Emulator config.ini restored (preserved across build)" - fi - else - info "Emulator config.ini preserved" - fi - if [ ! -f "$emu_cfg" ]; then - fail "No target/config.ini — emulator would start with defaults!" - ERRORS=$((ERRORS + 1)) - fi - [ -n "$emu_cfg_bak" ] && rm -rf "${emu_cfg_bak%/config.ini}" 2>/dev/null || true - local jar=$(find target -maxdepth 1 -name 'Polaris-*-jar-with-dependencies.jar' -printf '%T@ %p\n' 2>/dev/null | sort -rn | sed -n '1s/^[0-9.]* //p' | xargs -r basename 2>/dev/null || echo "") - [ -z "$jar" ] && die "No jar found" - EMULATOR_BUILT=true - ok "Jar: %s" "$jar" - cd target/ - cat > emulator << EOJ -#!/bin/sh -file_name_emulator=emulator.log -current_time=\$(date "+%H%M_%d-%m-%Y") -file_name=\$file_name_emulator.\$current_time -mv /var/log/emu/emulator.log /var/log/emu/\$file_name -java -Dfile.encoding=UTF8 -Xmx4G -jar $jar -EOJ - chmod +x emulator; ok "Launch script updated" - cd "$SCRIPT_DIR" - else - info "Emulator: already up to date" - fi -} - -update_renderer() { - step 3 9 "Update Octane-Renderer" - local grc=1 - if git_update "$NITRO_RENDERER" "$NITRO_BRANCH"; then - grc=0 - else - grc=$? - fi - if [ $grc -eq 2 ]; then - # Let the caller fail the parallel group; the state file records it. - warn "Renderer update failed (git error)" - return 1 - fi - if [ $grc -eq 0 ]; then - HAD_UPDATES=true; UPDATED_REPOS+=("Octane-Renderer") - info "Renderer pulled (%s)" "$(git -C "$NITRO_RENDERER" log --oneline -1 2>/dev/null || true)" - else - info "Renderer: already up to date" - fi - # Renderer is consumed by the Octane client build through the global - # `@nitrots/nitro-renderer` link — it needs its SOURCE updated (git pull - # above) and its node_modules present, nothing more. We deliberately do NOT - # rebuild or reinstall a working node_modules: repeated parallel yarn - # installs corrupt the cache and are how the client build silently breaks. - if [ -d "$NITRO_RENDERER/node_modules" ]; then - info "Renderer node_modules present — skipping yarn install" - else - spinner_start "Installing renderer deps (first run)..." - if yarn_install_repo "$NITRO_RENDERER" --frozen-lockfile --prefer-offline; then - spinner_stop ok; ok "Renderer dependencies installed" - elif yarn_install_repo "$NITRO_RENDERER"; then - spinner_stop ok; ok "Renderer dependencies installed (online)" - else - spinner_stop fail - warn "Renderer: node_modules still missing — Octane build may fail" - fi - fi - # Idempotent global registration (no install involved) so the client build - # can still `yarn link "@nitrots/nitro-renderer"`. Not fatal when absent. - if [ ! -e "$HOME/.config/yarn/link/@nitrots/nitro-renderer" ] \ - && [ ! -e "/usr/local/share/.config/yarn/link/@nitrots/nitro-renderer" ]; then - cd "$NITRO_RENDERER" - yarn link >> "$LOG_FILE" 2>&1 || warn "Renderer global link not registered" - cd "$SCRIPT_DIR" - fi - fix_perms - return 0 -} - -update_client() { - step 4 9 "Update & Build Octane" - local grc=1 - if git_update "$NITRO_CLIENT" "$NITRO_BRANCH"; then - grc=0 - else - grc=$? - fi - if [ $grc -eq 2 ]; then - warn "Octane update failed (git error)" - return 1 - elif [ $grc -eq 0 ]; then - HAD_UPDATES=true; NITRO_BUILT=true; UPDATED_REPOS+=("Octane") - cd "$NITRO_CLIENT" - backup_binaries - ROLLBACK_NEEDED=true - spinner_start "Installing deps..." - # yarn 1 can "succeed" while leaving node_modules incomplete, so verify - # that the vite build binary actually exists before attempting a build. - if ! yarn_install_repo "$NITRO_CLIENT" --frozen-lockfile --prefer-offline; then - warn "Octane: yarn install failed — cleaning node_modules and retrying" - yarn_reset_repo "$NITRO_CLIENT" - yarn_install_repo "$NITRO_CLIENT" || { spinner_stop fail; git_revert_repo "$NITRO_CLIENT"; die "yarn install failed (see: %s)" "$LOG_FILE"; } - fi - cd "$NITRO_CLIENT" - if [ ! -x node_modules/.bin/vite ]; then - warn "Octane: vite missing after install — resetting repo cache and doing full install" - # Parallel yarn installs can corrupt a shared cache; resetting the - # repo's own cache folder guarantees a clean fetch from the registry. - yarn_reset_repo "$NITRO_CLIENT" - yarn_install_repo "$NITRO_CLIENT" || { spinner_stop fail; git_revert_repo "$NITRO_CLIENT"; die "yarn install failed (see: %s)" "$LOG_FILE"; } - cd "$NITRO_CLIENT" - fi - spinner_stop ok - # Ensure renderer is linked into client - cd "$NITRO_CLIENT" - yarn link "@nitrots/nitro-renderer" >> "$LOG_FILE" 2>&1 || true - cd "$SCRIPT_DIR" - spinner_start "Building frontend..." - if yarn_run "$NITRO_CLIENT" build >> "$LOG_FILE" 2>&1; then - # Hard guarantee: the served entrypoint must exist, otherwise the - # client is broken. Revert the repo and restore the previous dist - # so the hotel keeps serving a working client. - if [ ! -f "$NITRO_CLIENT/dist/index.html" ]; then - spinner_stop fail - tail -30 "$LOG_FILE" || true - git_revert_repo "$NITRO_CLIENT" - die "Octane build produced no dist/index.html — reverted (see: %s)" "$LOG_FILE" - fi - spinner_stop ok - else - spinner_stop fail - tail -30 "$LOG_FILE" || true - git_revert_repo "$NITRO_CLIENT" - die "yarn build failed (see: %s)" "$LOG_FILE" - fi - else - info "Octane: already up to date" - fi - mkdir -p "$NITRO_CLIENT/dist/custom-themes" - [ ! -f "$NITRO_CLIENT/dist/custom-themes/index.json" ] && echo '{"themes":[]}' > "$NITRO_CLIENT/dist/custom-themes/index.json" - precompress_assets - fix_perms - return 0 -} - -# Does the CMS need a rebuild? True when the build output is missing or when -# any source/package file is newer than the last successful build. -needs_cms_build() { - local bid="$CMS_DIR/.next/BUILD_ID" - [ -f "$bid" ] || return 0 - local newer - newer=$(find "$CMS_DIR/src" "$CMS_DIR/package.json" "$CMS_DIR/pnpm-lock.yaml" \ - "$CMS_DIR/next.config.ts" "$CMS_DIR/next.config.mjs" "$CMS_DIR/next.config.js" \ - "$CMS_DIR/tsconfig.json" -newer "$bid" 2>/dev/null | head -1) - [ -n "$newer" ] -} - -# Build (and optionally pull) the CMS. Unlike the git repos this NEVER stashes -# local work: the CMS may carry uncommitted files that are intentionally live. -# Set NITRO_CMS_PULL=1 to also run a safe `git pull --ff-only` first. -update_cms() { - step 5 9 "Update & Build CMS" - [ -f "$CMS_DIR/package.json" ] || { info "No %s/package.json — skipping CMS" "$(basename "$CMS_DIR")"; return 0; } - local needs=false - if [ "$CMS_PULL" = "1" ] && [ -d "$CMS_DIR/.git" ]; then - local grc=1 - if git_update "$CMS_DIR" "$NITRO_BRANCH"; then - grc=0 - else - grc=$? - fi - if [ $grc -eq 2 ]; then - warn "CMS git update failed" - return 1 - fi - [ $grc -eq 0 ] && needs=true - elif [ "$CMS_PULL" != "1" ]; then - info "CMS git pull disabled (NITRO_CMS_PULL=0) — building from current tree" - fi - needs_cms_build && needs=true - if [ "$needs" = true ]; then - # Build into a staging dir and swap atomically: the live CMS keeps - # serving from .next the whole time, and a failed or interrupted - # build can never break the running site. - local staging="$CMS_DIR/.next-staging" - rm -rf "$staging" 2>/dev/null || true - spinner_start "Building CMS..." - local rc=0 - ( cd "$CMS_DIR" && NEXT_DIST_DIR=".next-staging" eval "$CMS_INSTALL_CMD" >> "$LOG_FILE" 2>&1 \ - && NEXT_DIST_DIR=".next-staging" eval "$CMS_BUILD_CMD" >> "$LOG_FILE" 2>&1 ) || rc=$? - if [ $rc -eq 0 ] && [ -s "$staging/BUILD_ID" ]; then - rm -rf "$CMS_DIR/.next" 2>/dev/null || true - mv "$staging" "$CMS_DIR/.next" 2>/dev/null - if [ -s "$CMS_DIR/.next/BUILD_ID" ]; then - spinner_stop ok - ok "CMS build complete" - CMS_BUILT=true - else - rm -rf "$staging" 2>/dev/null || true - spinner_stop fail - warn "CMS build swap failed — live build untouched" - fi - else - rm -rf "$staging" 2>/dev/null || true - spinner_stop fail - tail -40 "$LOG_FILE" | grep -E '(error|Error|ERROR|Failed|BUILD)' || true - warn "CMS build failed — live build untouched, see %s" "$LOG_FILE" - fi - else - info "CMS: already up to date" - fi - return 0 -} - -# Wait for the CMS (Next.js on port $CMS_PORT) to accept connections. -wait_for_cms_health() { - local hc=0 - while [ "$hc" -lt "$CMS_HEALTH_RETRIES" ]; do - if command -v nc &>/dev/null && nc -z 127.0.0.1 "$CMS_PORT" -w 3 2>/dev/null; then - ok "CMS health check passed (port %s listening)" "$CMS_PORT" - return 0 - fi - if timeout 3 bash -c "exec 3<>/dev/tcp/127.0.0.1/$CMS_PORT" 2>/dev/null; then - ok "CMS health check passed (port %s listening)" "$CMS_PORT" - return 0 - fi - hc=$((hc+1)) - [ "$hc" -lt "$CMS_HEALTH_RETRIES" ] && sleep "$CMS_HEALTH_INTERVAL" - done - warn "CMS not healthy after %s retries (port %s)" "$CMS_HEALTH_RETRIES" "$CMS_PORT" - return 1 -} - -sync_configs() { - step 6 9 "Sync Configurations" - mkdir -p "$GAMEDATA_CONF_DIR" - - # Backup current configs before overwriting - local cb=$(backup_configs) - info "Config back-up: %s" "$(basename "$cb")" - - local ms="$SCRIPT_DIR/scripts/merge-config.cjs" - local dcd="$NITRO_CLIENT/dist/configuration" - for d in "$NITRO_SRC_DIR" "$GAMEDATA_CONF_DIR"; do - [ -d "$d" ] && [ ! -w "$d" ] && sudo chown -R "$(whoami)":"$(whoami)" "$d" 2>/dev/null || true - done - local ec=0 - # Standard *.example files (ui-config, client-mode, etc.) - for ef in "$NITRO_SRC_DIR"/*.example; do - [ -f "$ef" ] || continue - local b=$(basename "$ef"); local tn="${b%.example}" - case "$tn" in *.*) ;; *) tn="$tn.json" ;; esac - node "$ms" "$ef" "$NITRO_SRC_DIR/$tn" 2>/dev/null - node "$ms" "$ef" "$GAMEDATA_CONF_DIR/$tn" 2>/dev/null - [ -d "$dcd" ] && cp "$NITRO_SRC_DIR/$tn" "$dcd/$tn" 2>/dev/null || true - ec=$((ec+1)) - done - # Repo ships renamed examples that don't match the standard *.example glob: - # renderer-config.json.example.json -> renderer-config.json - # renderer-config..jsonc.example.json -> renderer-config.jsonc - # (the ".." in that second name is a quirk of the upstream repo). Map them - # explicitly so we never emit garbage like renderer-config.jsonc.json. - local -A rc_map=( - ["renderer-config.json.example.json"]="renderer-config.json" - ["renderer-config..jsonc.example.json"]="renderer-config.jsonc" - ) - for ex in "${!rc_map[@]}"; do - local src="$NITRO_SRC_DIR/$ex" - [ -f "$src" ] || continue - local tgt="${rc_map[$ex]}" - node "$ms" "$src" "$NITRO_SRC_DIR/$tgt" 2>/dev/null || true - node "$ms" "$src" "$GAMEDATA_CONF_DIR/$tgt" 2>/dev/null || true - [ -d "$dcd" ] && cp "$NITRO_SRC_DIR/$tgt" "$dcd/$tgt" 2>/dev/null || true - ec=$((ec+1)) - done - ok "%s config file(s) synced" "$ec" - - # Validate the generated configs - validate_configs "$NITRO_SRC_DIR" - - # NOTE: legacy *.json5 leftovers in the config dirs are intentionally left - # in place — this updater never deletes user files, it only adds what is - # missing from the upstream examples. - - # Pre-flight: warn if NITRO_IMAGE_LIBRARY_URL looks wrong before applying configs - if [ -n "$NITRO_IMAGE_LIBRARY_URL" ]; then - case "$NITRO_IMAGE_LIBRARY_URL" in - *://*/nitro-client*|*://*/nitro-client/) - fail "NITRO_IMAGE_LIBRARY_URL points to the Nitro client path instead of /gamedata/c_images/ — images will 404" - ;; - esac - case "$NITRO_IMAGE_LIBRARY_URL" in - */c_images/) ;; - *://*) warn "NITRO_IMAGE_LIBRARY_URL does not contain /c_images/ — verify it points to the c_images directory" ;; - esac - fi - - info "Applying critical config URLs..." - # Use the node-based sync so JSONC configs (which contain // comments and - # URLs like https://) are parsed and rewritten safely. The previous Python - # json.load choked on the comments and silently skipped the URL injection, - # leaving the example.com placeholders in place. - NITRO_SRC_DIR="$NITRO_SRC_DIR" NITRO_DIST_CONFIG_DIR="$dcd" NITRO_GAMEDATA_CONF_DIR="$GAMEDATA_CONF_DIR" \ - NITRO_IMAGE_LIBRARY_URL="$NITRO_IMAGE_LIBRARY_URL" NITRO_HOF_FURNITURE_URL="$NITRO_HOF_FURNITURE_URL" \ - NITRO_API_URL="$NITRO_API_URL" NITRO_SOCKET_URL="$NITRO_SOCKET_URL" \ - NITRO_GAMEDATA_URL="$NITRO_GAMEDATA_URL" NITRO_ASSET_URL="$NITRO_ASSET_URL" \ - NITRO_FURNI_ASSET_ICON_URL="$NITRO_FURNI_ASSET_ICON_URL" \ - NITRO_URL_FORCE="${NITRO_URL_FORCE:-0}" \ - node "$SCRIPT_DIR/scripts/sync-nitro-urls.cjs" 2>/dev/null && ok "All config URLs synced" || warn "Config URL sync failed" - - # Flush Redis so new config is picked up immediately - if command -v redis-cli &>/dev/null; then - redis-cli FLUSHALL 2>/dev/null && ok "Redis cache flushed" || true - fi - fix_perms -} - -do_cleanup() { - step 7 9 "Cleanup" - local lc=$(find "$EMULATOR_DIR" -name "*.log" -mtime +14 2>/dev/null | wc -l) - find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null || true - # Clean old config backups (keep 10 most recent) - local cb_count=$(find "$CONFIG_BACKUP_DIR" -maxdepth 1 -type d ! -name 'rollback' 2>/dev/null | wc -l) - if [ "$cb_count" -gt 10 ]; then - find "$CONFIG_BACKUP_DIR" -maxdepth 1 -type d ! -name 'rollback' -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +11 | sed 's/^[0-9.]* //' | xargs -r rm -rf || true - ok "Old config backups pruned" - fi - [ -d "$BACKUP_DIR" ] && find "$BACKUP_DIR" -maxdepth 1 -name '*.sql' -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +6 | sed 's/^[0-9.]* //' | xargs -r rm -f || true - # NOTE: the per-repo yarn cache ($YARN_CACHE_DIR) is intentionally kept - # between updates — it is what makes parallel installs fast and reliable. - # It is only removed on an explicit "Clean" (menu) action. - yarn cache clean 2>/dev/null || true - rm -rf /tmp/nitro-* /tmp/epicnext-* 2>/dev/null || true - # Clean old Maven build artifacts (older than 7 days) - find "$EMULATOR_MODULE" -maxdepth 3 -name '*.jar' -mtime +7 2>/dev/null -exec rm -f {} \; || true - ok "Cleanup done (%s logs removed)" "$lc" -} - -fix_perms() { - # Make build output web-servable no matter which user ran the build. - # Running as root we chown directly; otherwise use `sudo -n` (no TTY, so it - # also works from cron). Never silently swallow failures. - local runner="" - [ "$(id -u)" -eq 0 ] || runner="sudo -n" - local changed=false - for d in "$NITRO_CLIENT" "$NITRO_RENDERER" "$GAMEDATA_CONF_DIR"; do - [ -d "$d" ] || continue - if $runner chown -R www-data:www-data "$d" 2>/dev/null; then - changed=true - else - warn "Could not chown %s to www-data:www-data" "$d" - fi - done - [ "$changed" = true ] && ok "Permissions set" -} - -do_permissions() { - step 8 9 "Set Permissions" - fix_perms -} - -# Wait for the emulator service to be active and its game port to be listening. -wait_for_emulator_health() { - local hc=0 - while [ "$hc" -lt "$HEALTH_RETRIES" ]; do - if service_active "$EMULATOR_SERVICE" && emulator_port_open; then - ok "Emulator health check passed (service up + port listening)" - return 0 - fi - hc=$((hc+1)) - [ "$hc" -lt "$HEALTH_RETRIES" ] && sleep "$HEALTH_INTERVAL" - done - return 1 -} - -# Resolve the newest built emulator jar filename inside the target directory. -emulator_jar_name() { - find "$EMULATOR_MODULE/target" -maxdepth 1 -name 'Polaris-*-jar-with-dependencies.jar' \ - -printf '%T@ %p\n' 2>/dev/null | sort -rn | sed -n '1s/^[0-9.]* //p' | xargs -r basename 2>/dev/null -} - -# Run the emulator's built-in Flyway repair against the database. This realigns -# flyway_schema_history with the packaged migrations (e.g. clearing "applied -# migration not resolved locally" drift) without touching hotel data. -flyaway_repair_db() { - local jar; jar=$(emulator_jar_name) - if [ -z "$jar" ]; then - fail "No emulator jar found in %s" "$EMULATOR_MODULE/target" - return 1 - fi - command -v java >/dev/null 2>&1 || { fail "Missing: java"; return 1; } - cd "$EMULATOR_MODULE/target" - spinner_start "Running Flyway repair..." - if timeout 300 java -Dfile.encoding=UTF8 -jar "$jar" --migrations=repair >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Flyway schema history repaired (see: %s)" "$LOG_FILE" - cd "$SCRIPT_DIR" - return 0 - fi - spinner_stop fail - tail -30 "$LOG_FILE" | grep -iE 'repair|migrat|error|exception' || true - cd "$SCRIPT_DIR" - return 1 -} - -# After a build/rebuild, ensure nginx redirects /gamedata/ (and /gamedata) -# to /gamedata/config/ instead of serving the raw directory listing. -# Idempotent: injects exact-match location blocks in front of the /gamedata/ -# prefix block, validates with `nginx -t` (restoring the pre-edit copy on -# failure) and keeps a timestamped backup. The actual reload happens at the -# end of do_restart. -ensure_gamedata_redirect() { - local root_cmd="" - [ "$(id -u)" -eq 0 ] || root_cmd="sudo -n" - local changed=false - local conf tmp snippet bak - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - if grep -qs "location = /gamedata" "$conf" 2>/dev/null; then - ok "Gamedata redirect already present in %s" "$(basename "$conf")" - continue - fi - tmp=$(mktemp) - mkdir -p /var/backups/nginx-cms 2>/dev/null || true - snippet=" # Redirect /gamedata/ to /gamedata/config/ - location = /gamedata { return 301 /gamedata/config/; } - location = /gamedata/ { return 301 /gamedata/config/; } -" - awk -v s="$snippet" ' - /^[[:space:]]*location \/gamedata\/ \{/ && !done { - printf "%s", s - done = 1 - } - { print } - ' "$conf" > "$tmp" 2>/dev/null - if [ ! -s "$tmp" ] || ! grep -qs "location = /gamedata" "$tmp" 2>/dev/null; then - rm -f "$tmp" 2>/dev/null || true - warn "Could not inject gamedata redirect into %s" "$conf" - continue - fi - bak="/var/backups/nginx-cms/$(basename "$conf").bak-$(date +%s)" - $root_cmd cp "$conf" "$bak" 2>/dev/null || { rm -f "$tmp"; warn "Cannot backup %s" "$conf"; continue; } - if ! $root_cmd install -m 640 "$tmp" "$conf" 2>/dev/null; then - rm -f "$tmp" 2>/dev/null || true - warn "Cannot update %s" "$conf" - continue - fi - rm -f "$tmp" 2>/dev/null || true - if ! sudo nginx -t >/dev/null 2>&1; then - $root_cmd cp "$bak" "$conf" 2>/dev/null || true - warn "nginx -t failed — reverted gamedata redirect in %s" "$conf" - continue - fi - changed=true - ok "Added /gamedata/ → /gamedata/config/ redirect to %s" "$(basename "$conf")" - done - return 0 -} - -# Resolve the emulator's public game (WebSocket) port — the port the Nitro -# client actually connects to (socket.url -> wss://ws. -> here). -# -# NOTE: Emulator config.ini ships a stale `game.port=3000` that does NOT speak -# the game WebSocket (it is an unused legacy listener); the real game socket -# listens on 2096. We therefore do NOT trust config.ini and instead: -# 1. explicit NITRO_EMULATOR_GAME_PORT override -# 2. auto-detect a listening port of the emulator java process that is not a -# well-known non-game port (3000/3001/3002/3030/3306/5432/6379/80/443/...). -# This reliably yields 2096. -# 3. fall back to 2096 (the conventional Nitro/Polaris game port). -emulator_game_port() { - [ -n "${NITRO_EMULATOR_GAME_PORT:-}" ] && { echo "$NITRO_EMULATOR_GAME_PORT"; return; } - local pid - pid=$(pgrep -f 'jar.*[Pp]olaris' 2>/dev/null | head -1) - [ -z "$pid" ] && pid=$(pgrep -x java 2>/dev/null | head -1) - if [ -n "$pid" ] && command -v ss >/dev/null 2>&1; then - local ports - ports=$(ss -ltnp 2>/dev/null | grep -E "pid=$pid," | grep -oE ':[0-9]+ ' | tr -d ' :' | sort -n -u) - for cand in $ports; do - case "$cand" in 80|443|3000|3001|3002|3030|3306|5432|6379|8080|8081|8422|9443|9444|5000|22|53|38939) continue ;; *) echo "$cand"; return ;; esac - done - fi - echo 2096 -} - -# Ensure nginx terminates the game WebSocket. The client connects to -# wss://ws. (port 443) and that has to be proxied to the emulator's -# game port, otherwise the client loads but never joins the server. Idempotent: -# injects a server block (reusing the existing SSL cert), validates with -# `nginx -t`, and reloads. Derives the domain automatically so this self-heals -# on any hotel VPS. -ensure_ws_proxy() { - [ ${#NGINX_SITE_CONFS[@]} -eq 0 ] && return 0 - local domain="${NITRO_DOMAIN:-}" - if [ -z "$domain" ]; then - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - domain=$(grep -oE 'server_name[^;]+;' "$conf" 2>/dev/null | head -1 \ - | tr -s ' ' '\n' | grep -vE 'server_name|;|127\.0\.0\.1|172\.|10\.|192\.168|localhost|\[?' | head -1) - [ -n "$domain" ] && break - done - fi - [ -z "$domain" ] && { warn "Could not derive domain for ws proxy — skipping"; return 0; } - local ws_name="ws.${domain}" - local gport; gport=$(emulator_game_port) - local root_cmd="" - [ "$(id -u)" -eq 0 ] || root_cmd="sudo -n" - mkdir -p /var/backups/nginx-cms 2>/dev/null || true - - local cert="" key="" - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - cert=$(grep -m1 'ssl_certificate[[:space:]]' "$conf" 2>/dev/null | awk '{print $2}' | tr -d ';') - key=$(grep -m1 'ssl_certificate_key[[:space:]]' "$conf" 2>/dev/null | awk '{print $2}' | tr -d ';') - [ -n "$cert" ] && [ -n "$key" ] && break - done - [ -z "$cert" ] && cert="/etc/ssl/epicnabbo-backend.pem" - [ -z "$key" ] && key="/etc/ssl/epicnabbo-backend.key" - - local conf - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - if grep -qs "$ws_name" "$conf" 2>/dev/null; then - ok "Game WebSocket proxy already present for %s" "$ws_name" - continue - fi - local tmp; tmp=$(mktemp) - local snippet - snippet="server { - listen 9443 ssl; - listen [::]:9443 ssl; - http2 on; - server_name $ws_name; - - ssl_certificate $cert; - ssl_certificate_key $key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers off; - - location / { - proxy_pass http://127.0.0.1:$gport; - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection \"upgrade\"; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - proxy_read_timeout 86400; - } -} -" - printf "%s\n" "$snippet" > "$tmp" - cat "$conf" >> "$tmp" 2>/dev/null - local bak="/var/backups/nginx-cms/$(basename "$conf").bak-ws-$(date +%s)" - $root_cmd cp "$conf" "$bak" 2>/dev/null || { rm -f "$tmp"; warn "Cannot backup %s" "$conf"; continue; } - if ! $root_cmd install -m 640 "$tmp" "$conf" 2>/dev/null; then - rm -f "$tmp" 2>/dev/null || true - warn "Cannot update %s" "$conf" - continue - fi - rm -f "$tmp" 2>/dev/null || true - if ! sudo nginx -t >/dev/null 2>&1; then - $root_cmd cp "$bak" "$conf" 2>/dev/null || true - warn "nginx -t failed — reverted ws proxy in %s" "$conf" - continue - fi - ok "Added game WebSocket proxy %s -> 127.0.0.1:%s" "$ws_name" "$gport" - done -} - -# Ensure the Octane client's emulator game API is reachable same-origin. The -# client calls /api/auth/* (login, server-key, sso-token, register, change-*, -# remember, refresh, ...) and /api/badges/custom via api.url (same origin = -# the CMS domain). The Polaris game server serves these on its game port (2096); -# the CMS does NOT. Proxying them here keeps api.url same-origin (no CORS) while -# still reaching the emulator. Inserted before the generic `location /api/` -# (which sends everything else to the CMS on :3002) inside the HTTPS vhost that -# listens on 9443. Idempotent: skips when the block is already present. -ensure_api_proxy() { - [ ${#NGINX_SITE_CONFS[@]} -eq 0 ] && return 0 - local gport; gport=$(emulator_game_port) - local root_cmd="" - [ "$(id -u)" -eq 0 ] || root_cmd="sudo -n" - mkdir -p /var/backups/nginx-cms 2>/dev/null || true - local snippet=" # --- Nitro/Emulator game API (managed by update-Nitrov3.sh: ensure_api_proxy) --- - # Octane client calls /api/auth/* and /api/badges/custom via api.url (same - # origin). The emulator serves these on its game port; proxy them so the - # client stays same-origin (no CORS). Inserted before the generic /api/. - location /api/auth/ { - proxy_pass http://127.0.0.1:${gport}; - proxy_http_version 1.1; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - add_header Cache-Control \"no-cache, no-store, must-revalidate\"; - } - location /api/badges/custom { - proxy_pass http://127.0.0.1:${gport}; - proxy_http_version 1.1; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - add_header Cache-Control \"no-cache, no-store, must-revalidate\"; - } -" - local conf - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - if grep -qs "location /api/auth/" "$conf" 2>/dev/null; then - ok "Emulator /api/auth proxy already present in %s" "$(basename "$conf")" - continue - fi - local bak="/var/backups/nginx-cms/$(basename "$conf").bak-api-$(date +%s)" - $root_cmd cp "$conf" "$bak" 2>/dev/null || { warn "Cannot backup %s" "$conf"; continue; } - # Insert the snippet before the first `location /api/ {` inside the - # server block that listens on 9443 (the HTTPS client vhost). - if ! SNIPPET="$snippet" $root_cmd perl -i -pe ' - BEGIN { $https=0; $inserted=0; $snip=$ENV{SNIPPET}; } - if (/^\s*server\s*\{/) { $https=0; } - if (/^\s*listen\s+9443\b/) { $https=1; } - if ($https && !$inserted && /^\s*location\s+\/api\/\s*\{/) { - $_ = $snip . $_; - $inserted=1; - } - ' "$conf" 2>/dev/null; then - $root_cmd cp "$bak" "$conf" 2>/dev/null || true - warn "Failed to insert emulator /api proxy into %s" "$(basename "$conf")" - continue - fi - if ! sudo nginx -t >/dev/null 2>&1; then - $root_cmd cp "$bak" "$conf" 2>/dev/null || true - warn "nginx -t failed — reverted emulator /api proxy in %s" "$(basename "$conf")" - continue - fi - ok "Added emulator /api proxy to %s" "$(basename "$conf")" - done -} - -# Ensure a catch-all default_server exists on the nginx vhost port. L7 proxies -# such as Traefik health-check the upstream using the IP as the Host header, -# which matches no real vhost; without this block those checks hit the first -# server block (e.g. the ws proxy) and get a 400, marking the site unhealthy -# (HTTP 503). Answering /health with 200 keeps proxy health checks green. -ensure_default_server() { - [ ${#NGINX_SITE_CONFS[@]} -eq 0 ] && return 0 - local found=false - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - if grep -qs "listen.*default_server" "$conf" 2>/dev/null; then found=true; break; fi - done - $found && { ok "Default-server health block already present"; return 0; } - local root_cmd="" - [ "$(id -u)" -eq 0 ] || root_cmd="sudo -n" - local cert="" key="" - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - cert=$(grep -m1 'ssl_certificate[[:space:]]' "$conf" 2>/dev/null | awk '{print $2}' | tr -d ';') - key=$(grep -m1 'ssl_certificate_key[[:space:]]' "$conf" 2>/dev/null | awk '{print $2}' | tr -d ';') - [ -n "$cert" ] && [ -n "$key" ] && break - done - [ -z "$cert" ] && cert="/etc/ssl/epicnabbo-backend.pem" - [ -z "$key" ] && key="/etc/ssl/epicnabbo-backend.key" - local conf - for conf in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$conf" ] || continue - local tmp; tmp=$(mktemp) - mkdir -p /var/backups/nginx-cms 2>/dev/null || true - cat > "$tmp" <> "$tmp" 2>/dev/null - local bak="/var/backups/nginx-cms/$(basename "$conf").bak-default-$(date +%s)" - $root_cmd cp "$conf" "$bak" 2>/dev/null || { rm -f "$tmp"; warn "Cannot backup %s" "$conf"; continue; } - if ! $root_cmd install -m 640 "$tmp" "$conf" 2>/dev/null; then - rm -f "$tmp" 2>/dev/null || true - warn "Cannot update %s" "$conf" - continue - fi - rm -f "$tmp" 2>/dev/null || true - if ! sudo nginx -t >/dev/null 2>&1; then - $root_cmd cp "$bak" "$conf" 2>/dev/null || true - warn "nginx -t failed — reverted default-server block in %s" "$conf" - continue - fi - ok "Added default-server /health block to %s" "$(basename "$conf")" - done -} - -do_restart() { - step 9 9 "Restart Services" - ensure_gamedata_redirect - ensure_default_server - ensure_ws_proxy - ensure_api_proxy - if systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then - # Only bounce the emulator when it was genuinely rebuilt this run, or - # when it is not actually serving — "nothing changed" stays online. - if [ "$EMULATOR_BUILT" = true ] || ! emulator_port_open; then - sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" || warn "Restart failed" - else - ok "$EMULATOR_SERVICE already healthy — no restart needed" - fi - if ! wait_for_emulator_health; then - warn "Emulator not healthy after %s retries" "$HEALTH_RETRIES" - sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 20 2>/dev/null || true - # Flyway schema drift (an applied migration no longer resolvable - # locally) aborts startup. Repair the history table and retry once - # before giving up, so updates survive upstream migration renames. - local flyway_drift=false - if sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 300 2>/dev/null \ - | grep -qiE 'applied migration not resolved locally|FlywayValidateException|Migrations have failed validation'; then - flyway_drift=true - warn "Flyway schema drift detected — running repair and retrying" - # Halt the restart loop so repair cannot race migrate on the history table - sudo systemctl stop "$EMULATOR_SERVICE" 2>/dev/null || true - if flyaway_repair_db; then - sudo systemctl start "$EMULATOR_SERVICE" 2>/dev/null || true - if wait_for_emulator_health; then - ok "Emulator healthy after Flyway repair" - flyway_drift=false - fi - else - sudo systemctl start "$EMULATOR_SERVICE" 2>/dev/null || true - fi - fi - if [ "$flyway_drift" = true ]; then - ERRORS=$((ERRORS + 1)) - ROLLBACK_NEEDED=true - fi - fi - elif command -v pm2 &>/dev/null; then - pm2 restart all 2>/dev/null && ok "PM2 restarted" || warn "PM2 restart failed" - else - warn "No service manager found" - fi - # CMS (Next.js under PM2): restart only when it was rebuilt this run, or - # when it is not answering — keep an already-online CMS untouched. - if command -v pm2 &>/dev/null && [ -f "$CMS_DIR/package.json" ]; then - local cms_restarted=false - if [ "$CMS_BUILT" = true ]; then - if pm2 restart "$CMS_PM2_NAME" 2>/dev/null; then - ok "$CMS_PM2_NAME restarted" - cms_restarted=true - elif pm2 restart all 2>/dev/null; then - ok "PM2 restarted" - cms_restarted=true - else - warn "PM2 restart failed for %s" "$CMS_PM2_NAME" - fi - elif ! wait_for_cms_health; then - warn "CMS unhealthy — restarting %s" "$CMS_PM2_NAME" - pm2 restart "$CMS_PM2_NAME" 2>/dev/null && ok "$CMS_PM2_NAME restarted" && cms_restarted=true || warn "PM2 restart failed" - else - ok "$CMS_PM2_NAME already healthy — no restart needed" - fi - if [ "$cms_restarted" = true ]; then - wait_for_cms_health || ERRORS=$((ERRORS + 1)) - fi - fi - # Reload nginx. Prefer `nginx -s reload` (the running master picks it up - # immediately); fall back to systemctl for hosts where nginx is a unit. - if command -v nginx &>/dev/null && sudo nginx -t >/dev/null 2>&1; then - if sudo nginx -s reload >/dev/null 2>&1 || sudo systemctl reload nginx >/dev/null 2>&1; then - ok "Nginx reloaded" - else - warn "Nginx reload failed" - fi - fi - command -v redis-cli &>/dev/null && redis-cli FLUSHALL 2>/dev/null && ok "Redis flushed" -} - -# Probe the emulator's configured TCP port to confirm it is actually serving, -# not merely that systemd reports the unit as active. -emulator_port_open() { - local port="${NITRO_EMULATOR_PORT:-}" - if [ -z "$port" ]; then - port=$(emulator_game_port) - fi - if command -v redis-cli >/dev/null 2>&1; then - local p; p=$(redis-cli GET nitro:emulator:port 2>/dev/null) - [ -n "$p" ] && port="$p" - fi - if command -v nc >/dev/null 2>&1; then - nc -z 127.0.0.1 "$port" -w 3 2>/dev/null && return 0 - fi - if command -v ss >/dev/null 2>&1; then - ss -ltn 2>/dev/null | grep -q ":$port " && return 0 - fi - timeout 3 bash -c "exec 3<>/dev/tcp/127.0.0.1/$port" 2>/dev/null && return 0 - return 1 -} - -show_summary() { - local el=$(($(date +%s) - START_TIME)); local ef; ef=$(printf '%dm %ds' $((el/60)) $((el%60))) - echo "" - echo -e " ${C_CYAN}╔══════════════════════════════════════════════════════════════════╗${C_RESET}" - if [ "$ERRORS" -eq 0 ]; then - echo -e " ${C_CYAN}║${C_RESET} ${C_GREEN}${C_BOLD}✔ $(_t "UPDATE COMPLETED SUCCESSFULLY")${C_RESET} ${C_CYAN}║${C_RESET}" - else - echo -e " ${C_CYAN}║${C_RESET} ${C_YELLOW}${C_BOLD}⚠ $(_t "COMPLETED WITH") $ERRORS $(_t "ERROR(S)")${C_RESET} ${C_CYAN}║${C_RESET}" - fi - echo -e " ${C_CYAN}╠══════════════════════════════════════════════════════════════════╣${C_RESET}" - printf " ${C_CYAN}║${C_RESET} $(_t "Duration:") %-46s${C_CYAN}║${C_RESET}\n" "$ef" - printf " ${C_CYAN}║${C_RESET} $(_t "Updates:") %-46s${C_CYAN}║${C_RESET}\n" "$([ "$HAD_UPDATES" = true ] && echo "$(_t "Yes")" || echo "$(_t "No")")" - printf " ${C_CYAN}║${C_RESET} $(_t "Octane build:") %-46s${C_CYAN}║${C_RESET}\n" "$([ "$NITRO_BUILT" = true ] && echo "$(_t "Yes")" || echo "$(_t "No")")" - printf " ${C_CYAN}║${C_RESET} $(_t "Warnings:") %-46s${C_CYAN}║${C_RESET}\n" "$WARNINGS" - [ ${#UPDATED_REPOS[@]} -gt 0 ] && printf " ${C_CYAN}║${C_RESET} $(_t "Repos:") %-46s${C_CYAN}║${C_RESET}\n" "${UPDATED_REPOS[*]}" - echo -e " ${C_CYAN}╚══════════════════════════════════════════════════════════════════╝${C_RESET}" - echo "" - notify "SUCCESS" "Completed in $ef (${WARNINGS} warnings)" -} - -# ============================================================================= -# FULL UPDATE -# ============================================================================= -cmd_update() { - echo "" - acquire_lock - DRY_RUN=${DRY_RUN:-false} - local mode_label="${SELECTIVE_UPDATE:-full}" - [ "$DRY_RUN" = true ] && mode_label="$mode_label [DRY-RUN]" - info "Site: %s | Branch: %s | Mode: %s" "${NITRO_SITE_URL:-?}" "$NITRO_BRANCH" "$mode_label" - info "Log: %s" "$LOG_FILE" - if [ "$DRY_RUN" = true ]; then - info "Dry-run: would update repos, run DB backups, build, sync configs, cleanup, restart services" - show_summary - release_lock - return - fi - - step 1 9 "System Diagnostics" - preflight_check - - case "${SELECTIVE_UPDATE:-full}" in - emulator) - update_emulator - # A freshly rebuilt jar must be loaded — bounce the service and - # wait for it to come back healthy. - if [ "$EMULATOR_BUILT" = true ] && systemctl cat "$EMULATOR_SERVICE" &>/dev/null; then - sudo systemctl restart "$EMULATOR_SERVICE" 2>/dev/null && ok "$EMULATOR_SERVICE restarted" - wait_for_emulator_health || warn "Emulator not healthy after %s retries" "$HEALTH_RETRIES" - fi - ;; - renderer) update_renderer ;; - client) update_client ;; - configs) sync_configs ;; - dev) - # Quick dev mode: skip builds, just sync configs and reload - info "Dev mode: skipping builds, syncing configs only" - sync_configs - do_restart - ;; - *) - RUN_ROLLBACK=true - update_emulator - # Always attempt the renderer + client builds in parallel. Each - # update_* function runs git_update itself and skips when its repo - # is already up to date, so gating these on the emulator's update - # status would wrongly skip them when only client/renderer changed. - parallel_run "renderer" update_renderer - parallel_run "client" update_client - parallel_wait - if ! parallel_failed; then - warn "Parallel build(s) failed: %s — aborting before restart" "${PARALLEL_FAILED[*]}" - ROLLBACK_NEEDED=true - die "Build failure in: %s" "${PARALLEL_FAILED[*]}" - fi - update_cms - sync_configs - do_cleanup - do_permissions - do_restart - RUN_ROLLBACK=false - [ -n "$BINARY_BACKUP_DIR" ] && rm -rf "$BINARY_BACKUP_DIR" 2>/dev/null || true - ;; - esac - - show_summary - fix_perms - release_lock -} - -# ============================================================================= -# BACKUP / RESTORE -# ============================================================================= -cmd_backup() { - step 1 1 "Database Backup" - mkdir -p "$BACKUP_DIR" - local bf="$BACKUP_DIR/manual_$(date +%Y%m%d_%H%M%S).sql" - spinner_start "Backing up..." - if $MYSQL_DUMP $MYSQL_CRED --force --skip-lock-tables --routines --events --triggers "$DB_NAME" > "$bf" 2>/dev/null; then - local sz=$(stat -c%s "$bf" 2>/dev/null || echo 0); spinner_stop ok - ok "Backup: %s — %s" "$(format_size "$sz")" "$(basename "$bf")" - else - # The DB user may lack routine/trigger/event privileges (common on - # restricted MySQL accounts). Retry data-only instead of failing, and - # never leave a half-written dump behind. - rm -f "$bf" 2>/dev/null || true - if $MYSQL_DUMP $MYSQL_CRED --force --skip-lock-tables "$DB_NAME" > "$bf" 2>/dev/null; then - local sz2=$(stat -c%s "$bf" 2>/dev/null || echo 0); spinner_stop ok - warn "Routines/triggers/events skipped (insufficient privileges)" - ok "Backup: %s — %s" "$(format_size "$sz2")" "$(basename "$bf")" - else - rm -f "$bf" 2>/dev/null || true - spinner_stop fail; die "Backup failed" - fi - fi -} - -cmd_restore() { - step 1 1 "Database Restore" - [ ! -d "$BACKUP_DIR" ] && die "No backup dir" - local backups=() - while IFS= read -r line; do backups+=("$line"); done < <(list_sorted "$BACKUP_DIR" '*.sql') - [ ${#backups[@]} -eq 0 ] && die "No backups" - echo "" - echo -e " ${C_BOLD}${C_CYAN}$(_t "Backups:")${C_RESET}" - local i=1 - for bk in "${backups[@]}"; do - local sz=$(format_size "$(stat -c%s "$bk" 2>/dev/null || echo 0)") - printf " ${C_GREEN}${C_BOLD}%2d)${C_RESET} %-40s ${C_DIM}%s${C_RESET}\n" "$i" "$(basename "$bk")" "$sz" - i=$((i+1)) - done - echo -en "\n $(_t "Select") [1-$((i-1))]: " - local c; read -rt 30 c - [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#backups[@]} ] || die "$(_t "Invalid")" - local sel="${backups[$((c-1))]}" - if ! confirm "Restore $(basename "$sel")? OVERWRITES database!"; then info "Cancelled"; return; fi - spinner_start "Restoring..." - $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" < "$sel" 2>/dev/null && spinner_stop ok && ok "Restored" || { spinner_stop fail; die "Restore failed"; } -} - -cmd_backups() { - step 1 1 "Backup Inventory" - [ ! -d "$BACKUP_DIR" ] && { warn "No backup dir"; return; } - echo "" - printf " ${C_DIM}%-4s %-38s %-10s${C_RESET}\n" "#" "FILENAME" "SIZE" - printf " ${C_DIM}%s${C_RESET}\n" "$(repeat '─' 56)" - local t=0 ts=0 - while IFS= read -r l; do - t=$((t+1)); local n=$(basename "$l"); local s=$(stat -c%s "$l" 2>/dev/null || echo 0); ts=$((ts+s)) - printf " ${C_GREEN}%2d${C_RESET} %-38s ${C_CYAN}%s${C_RESET}\n" "$t" "$n" "$(format_size "$s")" - done < <(list_sorted "$BACKUP_DIR" '*.sql') - printf " ${C_DIM}%s${C_RESET}\n" "$(repeat '─' 56)" - echo -e " ${C_BOLD}$(_t "Total:") $t $(_t "backups") — $(format_size "$ts")${C_RESET}\n" -} - -# ============================================================================= -# STATUS -# ============================================================================= -cmd_status() { - step 1 1 "System Status" - echo "" - box_top "SERVER" 56; box_kv "Host:" "$(hostname)" 56 - box_kv "OS:" "$(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '"' || uname -s)" 56 - box_kv "Uptime:" "$(uptime -p 2>/dev/null || uptime)" 56; box_bottom 56 - echo "" - box_top "RESOURCES" 56 - box_kv "CPU:" "$(awk '{printf "%.1f", $1}' /proc/loadavg 2>/dev/null || echo "?") / $(nproc 2>/dev/null || echo '?') cores" 56 - box_kv "Memory:" "$(free -m 2>/dev/null | awk '/^Mem:/{printf "%dMB / %dMB", $3, $2}')" 56 - box_kv "Disk:" "$(df -h "$SCRIPT_DIR" 2>/dev/null | tail -1 | awk '{printf "%s / %s (%s)", $3, $2, $5}')" 56 - local dbs=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT ROUND(SUM(data_length+index_length)/1024/1024,1) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?") - local dbt=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?") - box_kv "Database:" "${dbs}MB / ${dbt} tables" 56; box_bottom 56 - echo "" - box_top "SERVICES" 56 - for svc in nginx mariadb "$(redis_backend)" "$EMULATOR_SERVICE" php-fpm cron; do - local st="${C_DIM}N/A${C_RESET}" - service_active "$svc" && st="${C_GREEN}ACTIVE${C_RESET}" - printf " ${C_CYAN}║${C_RESET} %-20s %b${C_CYAN} ║${C_RESET}\n" "$svc" "$st" - done; box_bottom 56 - echo "" - box_top "GIT REPOS" 56 - for re in "Emulator:$EMULATOR_REPO" "Octane:$NITRO_CLIENT" "Render:$NITRO_RENDERER"; do - local nm="${re%%:*}" pa="${re##*:}" - [ -d "$pa/.git" ] || continue - local br=$(cd "$pa" && git branch --show-current 2>/dev/null || echo "?") - local cm=$(cd "$pa" && git log --oneline -1 2>/dev/null | cut -c1-30 || echo "?") - local ah=$(cd "$pa" && git rev-list --count "origin/$(git branch --show-current 2>/dev/null)"..HEAD 2>/dev/null || echo "0") - local bh=$(cd "$pa" && git rev-list --count HEAD.."origin/$(git branch --show-current 2>/dev/null)" 2>/dev/null || echo "0") - printf " ${C_CYAN}║${C_RESET} %-12s ${C_CYAN}%-8s${C_RESET} %s" "$nm" "$br" "$cm" - [ "$ah" -gt 0 ] && printf " ${C_RED}+${ah}${C_RESET}" || printf " " - [ "$bh" -gt 0 ] && printf " ${C_YELLOW}-${bh}${C_RESET}" || printf " " - printf " ${C_CYAN}║${C_RESET}\n" - done; box_bottom 56 - echo "" -} - -# ============================================================================= -# HEALTH CHECK -# ============================================================================= -cmd_health() { - step 1 1 "Health Check" - local t=0 p=0 f=0 w=0 - check() { t=$((t+1)); local lbl="$1"; shift; "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$lbl")"; } || { f=$((f+1)); fail "$(_t "$lbl")"; }; } - checkw() { t=$((t+1)); local lbl="$1"; shift; "$@" &>/dev/null && { p=$((p+1)); ok "$(_t "$lbl")"; } || { w=$((w+1)); warn "$(_t "$lbl")"; }; } - echo "" - echo -e " ${C_BOLD}$(_t "Commands:")${C_RESET}" - checkw "Git" command -v git; checkw "Node.js" command -v node; checkw "Yarn" command -v yarn - checkw "Python3" command -v python3; checkw "Java" command -v java; checkw "Maven" command -v mvn - checkw "MariaDB" command -v "$MYSQL_CLIENT" - echo -e "\n ${C_BOLD}$(_t "Script:")${C_RESET}" - checkw "$(_t "Bash syntax")" bash -n "$SCRIPT_DIR/$SCRIPT_NAME" - if command -v shellcheck &>/dev/null; then checkw "ShellCheck" shellcheck --quiet "$SCRIPT_DIR/$SCRIPT_NAME"; fi - if command -v checkbashisms &>/dev/null; then checkw "$(_t "Bashisms")" checkbashisms "$SCRIPT_DIR/$SCRIPT_NAME"; fi - echo -e "\n ${C_BOLD}$(_t "Configs:")${C_RESET}" - checkw "$(_t "Config dir")" test -d "$NITRO_SRC_DIR" - local cerr=0 - for cf in "$NITRO_SRC_DIR"/*.json; do - [ -f "$cf" ] || continue - case "$(basename "$cf")" in - *jsonc*|*.example*) continue ;; - esac - python3 -m json.tool "$cf" >/dev/null 2>&1 || cerr=$((cerr+1)) - done - [ "$cerr" -eq 0 ] && ok "$(_t "All JSON valid")" || warn "$(_t "JSON errors:") $cerr" - echo -e "\n ${C_BOLD}$(_t "Config URLs:")${C_RESET}" - for key in NITRO_SITE_URL NITRO_API_URL NITRO_SOCKET_URL NITRO_GAMEDATA_URL NITRO_IMAGE_LIBRARY_URL; do - local val="${!key}" - if [ -z "$val" ]; then - warn "$key" - else - case "$key" in - NITRO_IMAGE_LIBRARY_URL) - case "$val" in - */c_images/) ok "$key" ;; - *) warn "$key missing /c_images/ in path: $val" ;; - esac ;; - NITRO_SITE_URL) - case "$val" in - */nitro-client*|*/nitro-client) warn "$key should be the site URL, not the Nitro client path" ;; - *) ok "$key" ;; - esac ;; - *) ok "$key" ;; - esac - fi - done - echo -e "\n ${C_BOLD}$(_t "Directories:")${C_RESET}" - checkw "Emulator" test -d "$EMULATOR_MODULE"; checkw "Octane" test -d "$NITRO_CLIENT"; checkw "Renderer" test -d "$NITRO_RENDERER" - checkw "$(_t "Gamedata config")" test -d "$GAMEDATA_CONF_DIR"; checkw "$(_t "Backup dir")" test -d "$BACKUP_DIR" - echo -e "\n ${C_BOLD}$(_t "Services:")${C_RESET}" - checkw "MariaDB" service_active mariadb; checkw "Redis" redis_active; checkw "Nginx" service_active nginx - checkw "$EMULATOR_SERVICE" service_active "$EMULATOR_SERVICE" - echo -e "\n ${C_BOLD}$(_t "Network:")${C_RESET}" - checkw "$(_t "Nginx config")" nginx -t 2>/dev/null - if command -v redis-cli &>/dev/null; then checkw "Redis PING" redis-cli PING 2>/dev/null; fi - checkw "$(_t "Site reachable")" timeout 5 curl -sf "${NITRO_SITE_URL:-}" 2>/dev/null - if emulator_port_open; then ok "$(_t "Emulator port open")"; else warn "$(_t "Emulator port closed")"; fi - echo -e "\n ${C_BOLD}$(_t "Databases:")${C_RESET}" - checkw "$(_t "MySQL connect")" $MYSQL_CLIENT $MYSQL_CRED -e "SELECT 1" "$DB_NAME" 2>/dev/null - local dbt=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?") - t=$((t+1)); [ "$dbt" != "?" ] && { p=$((p+1)); ok "$(_t "Tables:") $dbt"; } || { f=$((f+1)); fail "$(_t "Tables:") ?"; } - echo -e "\n ${C_BOLD}$(_t "Resources:")${C_RESET}" - local ak=$(df --output=avail "$EMULATOR_DIR" 2>/dev/null | tail -1); local ag=$((ak/1024/1024)) - t=$((t+1)); [ "$ag" -ge "$MIN_DISK_GB" ] && { p=$((p+1)); ok "$(_t "Disk:") ${ag}GB"; } || { f=$((f+1)); fail "$(_t "Disk:") ${ag}GB"; } - local mem=$(free -m 2>/dev/null | awk '/^Mem:/{printf "%dMB / %dMB", $3, $2}') - t=$((t+1)); ok "$(_t "Memory:") $mem" - local load=$(awk '{printf "%.1f", $1}' /proc/loadavg 2>/dev/null || echo "?") - local cores=$(nproc 2>/dev/null || echo "?") - t=$((t+1)); ok "$(_t "Load:") $load / $cores cores" - echo "" - local sc=$((p*100/t)) - box_top "SCORE" 50 - box_kv "Passed:" "$p / $t" 50 - box_kv "Failed:" "$f" 50; box_kv "Warnings:" "$w" 50 - if [ "$sc" -ge 90 ]; then box_line "${C_GREEN}${C_BOLD}${sc}% — $(_t "EXCELLENT")${C_RESET}" 50 - elif [ "$sc" -ge 70 ]; then box_line "${C_YELLOW}${C_BOLD}${sc}% — $(_t "GOOD")${C_RESET}" 50 - else box_line "${C_RED}${C_BOLD}${sc}% — $(_t "NEEDS ATTENTION")${C_RESET}" 50; fi - box_bottom 50; echo "" -} - -# ============================================================================= -# FLYAWAY REPAIR -# ============================================================================= -cmd_flyaway_repair() { - echo "" - acquire_lock - DRY_RUN=${DRY_RUN:-false} - info "Site: %s | Branch: %s | Mode: %s" "${NITRO_SITE_URL:-?}" "$NITRO_BRANCH" "flyaway-repair" - info "Log: %s" "$LOG_FILE" - if [ "$DRY_RUN" = true ]; then - info "Dry-run: would check repos, reinstall deps, fix permissions, sync configs, restart services" - show_summary - release_lock - return - fi - - step 1 7 "System Diagnostics" - preflight_check - - step 2 7 "$(_t "Flyaway Repair")" - local repaired=0 - - for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do - [ -d "$repo/.git" ] || continue - local name; name=$(basename "$repo") - info "Checking %s..." "$name" - - # Reset any dirty/uncommitted changes that may cause issues - cd "$repo" - if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then - warn "Dirty working tree in %s — stashing changes" "$name" - git stash --include-untracked 2>/dev/null || true - fi - - # Reset to clean latest of the current branch - local branch; branch=$(git branch --show-current 2>/dev/null || echo "main") - if git checkout "$branch" 2>/dev/null; then - git checkout . 2>/dev/null || true - git clean -fd 2>/dev/null || true - ok "Cleaned %s working tree" "$name" - fi - cd "$SCRIPT_DIR" - - # Reinstall dependencies if package.json exists - if [ -f "$repo/package.json" ]; then - spinner_start "Installing deps for %s..." "$name" - if yarn_install_repo "$repo" --frozen-lockfile --prefer-offline 2>/dev/null; then - spinner_stop ok - ok "Deps reinstalled for %s" "$name" - else - spinner_stop fail - warn "yarn install failed for %s — resetting repo and retrying" "$name" - yarn_reset_repo "$repo" - yarn_install_repo "$repo" --frozen-lockfile --prefer-offline 2>/dev/null || warn "yarn install still failed for %s" "$name" - fi - fi - - # Re-link renderer: register global link + link into client - if [ "$repo" = "$NITRO_RENDERER" ] && [ -d "$repo" ]; then - spinner_start "Re-linking renderer..." - cd "$NITRO_RENDERER" - yarn link >> "$LOG_FILE" 2>&1 || warn "yarn link in renderer failed" - cd "$SCRIPT_DIR" - cd "$NITRO_CLIENT" - yarn link "@nitrots/nitro-renderer" >> "$LOG_FILE" 2>&1 || warn "yarn link to renderer failed" - cd "$SCRIPT_DIR" - spinner_stop ok - ok "Renderer re-linked" - fi - - repaired=$((repaired + 1)) - done - - # Ensure .nitro-build.json exists - if [ ! -f "$NITRO_CLIENT/.nitro-build.json" ]; then - echo '{"jsonMode":"jsonc"}' > "$NITRO_CLIENT/.nitro-build.json" - ok "Created .nitro-build.json (jsonc mode)" - fi - - # Realign Flyway schema history with the packaged migrations - step 3 7 "$(_t "Flyway DB Repair")" - if flyaway_repair_db; then - ok "$(_t "Flyway schema history repaired")" - else - fail "$(_t "Flyway DB repair failed — the emulator will retry on restart")" - fi - - # Fix permissions - step 4 7 "$(_t "Set Permissions")" - fix_perms - ok "Permissions fixed" - - # Sync / validate configs - step 5 7 "$(_t "Sync Configurations")" - sync_configs - - # Rebuild frontend if dist is missing or empty - if [ ! -d "$NITRO_CLIENT/dist" ] || [ -z "$(ls -A "$NITRO_CLIENT/dist" 2>/dev/null)" ]; then - step 6 7 "$(_t "Rebuild Frontend")" - spinner_start "$(_t "Building frontend...")" - cd "$NITRO_CLIENT" - if yarn_run "$NITRO_CLIENT" build >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Frontend rebuilt" - else - spinner_stop fail - fail "Frontend rebuild failed" - fi - cd "$SCRIPT_DIR" - else - ok "Frontend dist intact — skip rebuild" - fi - - # Rebuild emulator if jar is missing - if [ ! -d "$EMULATOR_MODULE/target" ] || ! ls "$EMULATOR_MODULE"/target/Polaris-*-jar-with-dependencies.jar &>/dev/null 2>&1; then - step 7 7 "$(_t "Rebuild Emulator")" - cd "$EMULATOR_MODULE" - backup_binaries - ROLLBACK_NEEDED=true - spinner_start "$(_t "Building emulator (parallel)...")" - command -v ccache >/dev/null 2>&1 && export CC="ccache gcc" CXX="ccache g++" - if mvn package -q -T 1C -Dmaven.test.skip=true >> "$LOG_FILE" 2>&1; then - spinner_stop ok - ok "Emulator rebuilt" - else - spinner_stop fail - fail "Emulator rebuild failed" - fi - cd "$SCRIPT_DIR" - else - ok "Emulator jar intact — skip rebuild" - fi - - # Restart services - do_restart - - show_summary - fix_perms - release_lock -} - -# ============================================================================= -# AUTO-SETUP COMMAND -# ============================================================================= -cmd_setup() { - step 1 1 "Setup" - echo "" - acquire_lock - info "Site: %s | Branch: %s | Mode: %s" "${NITRO_SITE_URL:-?}" "$NITRO_BRANCH" "auto-setup" - info "Log: %s" "$LOG_FILE" - - auto_setup_missing_repos - - release_lock -} - -# ============================================================================= -# SERVICES -# ============================================================================= -cmd_services() { - step 1 1 "Service Manager" - echo "" - echo -e " ${C_BOLD}1)${C_RESET} $(_t "Start Emulator") ${C_BOLD}2)${C_RESET} $(_t "Stop Emulator") ${C_BOLD}3)${C_RESET} $(_t "Restart Emulator")" - echo -e " ${C_BOLD}4)${C_RESET} $(_t "Start Nginx") ${C_BOLD}5)${C_RESET} $(_t "Restart Nginx") ${C_BOLD}6)${C_RESET} $(_t "Status All")" - echo -e " ${C_BOLD}7)${C_RESET} $(_t "Emulator Logs") ${C_BOLD}8)${C_RESET} $(_t "Emulator Journal") ${C_BOLD}0)${C_RESET} $(_t "Return")" - echo -en "\n $(_t "Select:") "; local c; read -rt 30 c - case "$c" in - 1) sudo systemctl start "$EMULATOR_SERVICE" && ok "Started" || fail "Failed" ;; - 2) sudo systemctl stop "$EMULATOR_SERVICE" && ok "Stopped" || fail "Failed" ;; - 3) sudo systemctl restart "$EMULATOR_SERVICE" && ok "Restarted" || fail "Failed" ;; - 4) sudo systemctl start nginx && ok "Started" || fail "Failed" ;; - 5) sudo systemctl restart nginx && ok "Restarted" || fail "Failed" ;; - 6) for s in nginx mariadb redis-server "$EMULATOR_SERVICE" php-fpm cron; do - local st="${C_RED}$(_t "DOWN")${C_RESET}"; service_active "$s" && st="${C_GREEN}$(_t "UP")${C_RESET}" - printf " %-25s %b\n" "$s" "$st" - done ;; - 7) sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 30 ;; - 8) sudo journalctl -u "$EMULATOR_SERVICE" --no-pager -n 100 --output=short-full ;; - esac -} - -# ============================================================================= -# DATABASE -# ============================================================================= -cmd_database() { - step 1 1 "Database Tools" - echo "" - echo -e " ${C_BOLD}1)${C_RESET} $(_t "DB Info") ${C_BOLD}2)${C_RESET} $(_t "List Tables") ${C_BOLD}3)${C_RESET} $(_t "Table Sizes")" - echo -e " ${C_BOLD}4)${C_RESET} $(_t "Optimize") ${C_BOLD}5)${C_RESET} $(_t "Custom Query") ${C_BOLD}6)${C_RESET} $(_t "Active Users")" - echo -e " ${C_BOLD}7)${C_RESET} $(_t "Processlist") ${C_BOLD}0)${C_RESET} $(_t "Return")" - echo -en "\n $(_t "Select:") "; local c; read -rt 30 c - case "$c" in - 1) box_top "DB INFO" 50 - local dbs=$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT ROUND(SUM(data_length+index_length)/1024/1024,1) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo "?") - box_kv "Name:" "$DB_NAME" 50; box_kv "Host:" "$DB_HOST:$DB_PORT" 50; box_kv "Size:" "${dbs}MB" 50 - box_kv "Tables:" "$($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB_NAME'" "$DB_NAME" 2>/dev/null || echo '?')" 50 - box_bottom 50 ;; - 2) $MYSQL_CLIENT $MYSQL_CRED -e "SHOW TABLES FROM $DB_NAME" 2>/dev/null ;; - 3) $MYSQL_CLIENT $MYSQL_CRED -e "SELECT table_name AS 'Table', ROUND(data_length/1024/1024,2) AS 'Data MB', ROUND(index_length/1024/1024,2) AS 'Index MB' FROM information_schema.tables WHERE table_schema='$DB_NAME' ORDER BY (data_length+index_length) DESC" 2>/dev/null ;; - 4) spinner_start "Optimizing..." - for tbl in $($MYSQL_CLIENT $MYSQL_CRED -N -e "SELECT table_name FROM information_schema.tables WHERE table_schema='$DB_NAME' AND engine='InnoDB'" "$DB_NAME" 2>/dev/null); do - $MYSQL_CLIENT $MYSQL_CRED -e "OPTIMIZE TABLE $DB_NAME.\`$tbl\`" "$DB_NAME" &>/dev/null || true - done; spinner_stop ok; ok "Optimized" ;; - 5) echo -en " SQL: "; local q; read -rt 30 q; [ -n "$q" ] && $MYSQL_CLIENT $MYSQL_CRED "$DB_NAME" -e "$q" 2>/dev/null ;; - 6) $MYSQL_CLIENT $MYSQL_CRED -e "SELECT name, motto FROM $DB_NAME.users WHERE online='1'" 2>/dev/null || warn "Could not query" ;; - 7) $MYSQL_CLIENT $MYSQL_CRED -e "SHOW FULL PROCESSLIST" 2>/dev/null || warn "Could not query" ;; - esac -} - -# ============================================================================= -# CONFIG -# ============================================================================= -cmd_config() { - step 1 1 "Configuration" - echo "" - box_top "CONFIGURATION" 56 - box_kv "Site:" "${NITRO_SITE_URL:-?}" 56 - box_kv "Branch:" "$NITRO_BRANCH" 56 - box_kv "Emulator:" "$EMULATOR_DIR" 56 - box_kv "Octane:" "$NITRO_CLIENT" 56 - box_kv "Renderer:" "$NITRO_RENDERER" 56 - box_kv "Database:" "$DB_NAME @ $DB_HOST:$DB_PORT" 56 - box_kv "Service:" "$EMULATOR_SERVICE" 56 - box_kv "Socket:" "${NITRO_SOCKET_URL:-?}" 56 - box_kv "API:" "${NITRO_API_URL:-?}" 56 - box_kv "Gamedata:" "${NITRO_GAMEDATA_URL:-?}" 56 - box_kv "Min Disk:" "${MIN_DISK_GB}GB" 56 - box_bottom 56; echo "" -} - -# ============================================================================= -# LOGS -# ============================================================================= -cmd_logs() { - step 1 1 "Log Viewer" - [ ! -d "$LOG_DIR" ] && { warn "No log dir"; return; } - local logs=() - while IFS= read -r l; do logs+=("$l"); done < <(list_sorted "$LOG_DIR" 'update-*.log') - [ ${#logs[@]} -eq 0 ] && { warn "No logs"; return; } - echo "" - local i=1 - for l in "${logs[@]}"; do - local sz=$(format_size "$(stat -c%s "$l" 2>/dev/null || echo 0)") - printf " ${C_GREEN}${C_BOLD}%2d)${C_RESET} %-35s ${C_DIM}%s${C_RESET}\n" "$i" "$(basename "$l")" "$sz" - i=$((i+1)) - done - echo -en "\n $(_t "Select") [1-$((i-1))]: "; local c; read -rt 30 c - if [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -ge 1 ] && [ "$c" -le ${#logs[@]} ]; then - less -R "${logs[$((c-1))]}" 2>/dev/null || cat "${logs[$((c-1))]}" - fi -} - -# ============================================================================= -# CLEAN -# ============================================================================= -cmd_clean() { - step 1 1 "Cache Cleanup" - echo "" - echo -e " ${C_BOLD}1)${C_RESET} $(_t "Yarn") ${C_BOLD}2)${C_RESET} $(_t "Next.js") ${C_BOLD}3)${C_RESET} $(_t "Redis")" - echo -e " ${C_BOLD}4)${C_RESET} $(_t "Logs") ${C_BOLD}5)${C_RESET} $(_t "Everything") ${C_BOLD}6)${C_RESET} $(_t "Docker")" - echo -e " ${C_BOLD}7)${C_RESET} $(_t "Maven") ${C_BOLD}0)${C_RESET} $(_t "Return")" - echo -en "\n $(_t "Select:") "; local c; read -rt 30 c - case "$c" in - 1) yarn cache clean 2>/dev/null; rm -rf "$YARN_CACHE_DIR" 2>/dev/null; ok "Yarn cleaned" ;; - 2) cd "$SCRIPT_DIR" && rm -rf .next 2>/dev/null && ok "Next.js cache cleared" ;; - 3) redis-cli FLUSHALL 2>/dev/null && ok "Redis flushed" || warn "Redis N/A" ;; - 4) find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null; ok "Logs cleaned" ;; - 5) yarn cache clean 2>/dev/null; rm -rf "$YARN_CACHE_DIR" 2>/dev/null; find "$EMULATOR_DIR" -name "*.log" -mtime +14 -exec rm -f {} \; 2>/dev/null; rm -rf /tmp/nitro-* /tmp/epicnext-* 2>/dev/null; ok "Full cleanup" ;; - 6) command -v docker &>/dev/null && docker system prune -f 2>/dev/null && ok "Docker pruned" || warn "Docker N/A" ;; - 7) find "$EMULATOR_MODULE" -maxdepth 3 -name '*.jar' -mtime +7 -exec rm -f {} \; 2>/dev/null; ok "Old Maven artifacts cleaned" ;; - esac -} - -# ============================================================================= -# GIT LOG -# ============================================================================= -cmd_gitlog() { - step 1 1 "Git Commit History" - echo "" - echo -e " ${C_BOLD}1)${C_RESET} $(_t "Emulator") ${C_BOLD}2)${C_RESET} $(_t "Octane") ${C_BOLD}3)${C_RESET} $(_t "Renderer")" - echo -en "\n $(_t "Select:") "; local c; read -rt 30 c - local repo="" - case "$c" in 1) repo="$EMULATOR_REPO" ;; 2) repo="$NITRO_CLIENT" ;; 3) repo="$NITRO_RENDERER" ;; *) return ;; esac - [ ! -d "$repo/.git" ] && { warn "Not a git repo"; return; } - echo "" - local branch - branch=$(cd "$repo" && git branch --show-current 2>/dev/null || echo "?") - local ahead - ahead=$(cd "$repo" && git rev-list --count "origin/${branch}"..HEAD 2>/dev/null || echo "0") - echo -e " ${C_BOLD}$(_t "Last 20 commits on") ${C_CYAN}${branch}${C_RESET} ($(_t "ahead:") ${C_RED}${ahead}${C_RESET}):" - echo "" - cd "$repo" && git log --pretty=format:" ${C_CYAN}%h${C_RESET} %C(yellow)%cr${C_RESET} %s" -20 2>/dev/null - echo -e "\n" -} - -# ============================================================================= -# BRANCH COMPARE -# ============================================================================= -cmd_compare() { - step 1 1 "Branch Comparison" - echo "" - local branches=() - load_branches branches - [ ${#branches[@]} -lt 2 ] && { warn "Need 2+ branches (found ${#branches[@]})"; return; } - echo -e " ${C_BOLD}$(_t "Branches:")${C_RESET}" - local i=1 - for b in "${branches[@]}"; do echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b"; i=$((i+1)); done - echo -en "\n $(_t "Branch A:") "; local ba; read -rt 30 ba - echo -en " $(_t "Branch B:") "; local bb; read -rt 30 bb - [[ "$ba" =~ ^[0-9]+$ ]] && [[ "$bb" =~ ^[0-9]+$ ]] && [ "$ba" -ge 1 ] && [ "$bb" -ge 1 ] && [ "$ba" -le ${#branches[@]} ] && [ "$bb" -le ${#branches[@]} ] || { warn "Invalid"; return; } - local bA="${branches[$((ba-1))]}" bB="${branches[$((bb-1))]}" - echo "" - for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do - [ -d "$repo/.git" ] || continue - echo -e " ${C_BOLD}$(basename "$repo"):${C_RESET}" - cd "$repo" 2>/dev/null || continue - if git rev-parse --verify "$bA" &>/dev/null && git rev-parse --verify "$bB" &>/dev/null; then - local ahead=$(git rev-list --count "$bB".."$bA" 2>/dev/null || echo "0") - local behind=$(git rev-list --count "$bA".."$bB" 2>/dev/null || echo "0") - echo -e " $bA $(_t "is") ${C_GREEN}$ahead${C_RESET} $(_t "ahead,") ${C_YELLOW}$behind${C_RESET} $(_t "behind") $bB" - fi - done; echo "" -} - -# ============================================================================= -# CRON SCHEDULER -# ============================================================================= -cmd_schedule() { - step 1 1 "Cron Scheduler" - echo "" - local cc=$(crontab -l 2>/dev/null | grep -F "$SCRIPT_NAME" || echo "") - echo -e " ${C_BOLD}$(_t "Current:")${C_RESET} ${cc:-${C_DIM}$(_t "none")${C_RESET}}" - echo "" - echo -e " ${C_BOLD}1)${C_RESET} $(_t "Daily 03:00") ${C_BOLD}2)${C_RESET} $(_t "Daily 04:00") ${C_BOLD}3)${C_RESET} $(_t "Weekly Sun 03:00")" - echo -e " ${C_BOLD}4)${C_RESET} $(_t "Custom time") ${C_BOLD}5)${C_RESET} $(_t "Remove") ${C_BOLD}0)${C_RESET} $(_t "Return")" - echo -en "\n $(_t "Select:") "; local c; read -rt 30 c - case "$c" in - 1) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 03:00" ;; - 2) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 4 * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled daily 04:00" ;; - 3) (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "0 3 * * 0 cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled weekly" ;; - 4) echo -en " $(_t "Minute") [0-59]: "; local m; read -rt 30 m - echo -en " $(_t "Hour") [0-23]: "; local h; read -rt 30 h - (crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME"; echo "$m $h * * * cd $SCRIPT_DIR && ./$SCRIPT_NAME update --branch $NITRO_BRANCH >> /var/log/nitro-cron.log 2>&1") | crontab - && ok "Scheduled $m $h * * *" ;; - 5) crontab -l 2>/dev/null | grep -vF "$SCRIPT_NAME" | crontab - && ok "Removed" ;; - esac -} - -# ============================================================================= -# GIT STASH VIEWER -# ============================================================================= -cmd_stash() { - step 1 1 "Git Stashes" - local found=false - for repo in "$EMULATOR_REPO" "$NITRO_CLIENT" "$NITRO_RENDERER"; do - [ -d "$repo/.git" ] || continue - local list - list=$(cd "$repo" && git stash list 2>/dev/null) - if [ -n "$list" ]; then - found=true - echo "" - echo -e " ${C_BOLD}$(basename "$repo"):${C_RESET}" - echo "$list" | while IFS= read -r line; do echo -e " ${C_CYAN}→${C_RESET} $line"; done - fi - done - $found || warn "No stashes found" - echo "" -} - -# ============================================================================= -# MAIN MENU -# ============================================================================= -pick_branch_and_update() { - local mode_label="Full" - case "${SELECTIVE_UPDATE:-full}" in - emulator) mode_label="Emulator Only" ;; client) mode_label="Client Only" ;; - renderer) mode_label="Renderer Only" ;; configs) mode_label="Configs Only" ;; - dev) mode_label="Dev Mode" ;; - esac - local branches=() - load_branches branches - echo "" - echo -e " ${C_BOLD}${C_CYAN}$(_t "Update:") ${C_GREEN}$mode_label${C_RESET}" - echo "" - local i=1 - for b in "${branches[@]}"; do - local m=""; [ "$b" = "$NITRO_BRANCH" ] && m=" ${C_GREEN}(current)${C_RESET}" - echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} ${C_CYAN}$b${C_RESET}$m" - i=$((i+1)) - done - echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $(_t "Custom")" - echo -en "\n $(_t "Branch") [1-$i]: " - local bc; read -rt 30 bc - if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then - NITRO_BRANCH="${branches[$((bc-1))]}" - elif [ "$bc" = "$i" ]; then - echo -en " $(_t "Name:") "; read -rt 30 NITRO_BRANCH; [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main" - else - warn "Invalid, using: %s" "$NITRO_BRANCH" - fi - echo -e "\n $(_t "Branch:") ${C_CYAN}${C_BOLD}$NITRO_BRANCH${C_RESET} $(_t "Mode:") ${C_GREEN}$mode_label${C_RESET}" - if ! confirm "Start update?"; then info "Cancelled"; return; fi - cmd_update -} - -cmd_interactive() { - local branches=() - load_branches branches - while true; do - clear - echo -e "${C_CYAN}" - echo "" - echo " ╔═══════════════════════════════════════════════════════════════════════════╗" - echo " ║ ║" - echo " ║ ██████╗ ██████╗ ██████╗ ████████╗██╗ ██╗ ██████╗ ██████╗ ║" - echo " ║ ██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗ ║" - echo " ║ ██████╔╝██████╔╝██████╔╝ ██║ ███████║██║ ██║██████╔╝ ║" - echo " ║ ██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══██║██║ ██║██╔══██╗ ║" - echo " ║ ██║ ██║ ██║██████╔╝ ██║ ██║ ██║╚██████╔╝██║ ██║ ║" - echo " ║ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ║" - echo " ║ ║" - echo " ╠═══════════════════════════════════════════════════════════════════════════╣" - echo -e " ║ ${C_GOLD}${C_BOLD} SMaRT v${SCRIPT_VERSION} — Emulator / Octane / Octane-Renderer${C_CYAN} ║" - echo -e " ║ ${C_WHITE}${C_BOLD} Updater & Manager by Remco — epicnabbo.nl${C_CYAN} ║" - echo " ╠═══════════════════════════════════════════════════════════════════════════╣" - echo -e " ║ ${C_DIM}$(_t "Branch:") ${C_CYAN}${NITRO_BRANCH}${C_DIM} (${#branches[@]} $(_t "branches")) │ $(date '+%H:%M')${C_CYAN} ║" - echo " ╚═══════════════════════════════════════════════════════════════════════════╝" - echo -e "${C_RESET}" - - echo -e " ${C_BG_GREEN}${C_WHITE}${C_BOLD} $(_t "UPDATE") ${C_RESET}" - echo -e " ${C_GREEN}${C_BOLD}1)${C_RESET} ${E_ROCKET} $(_t "Quick Update — current branch")" - echo -e " ${C_GREEN}${C_BOLD}2)${C_RESET} ${E_BOLT} $(_t "Full Update (choose branch)")" - echo -e " ${C_GREEN}${C_BOLD}3)${C_RESET} ${E_GEAR} $(_t "Emulator Only")" - echo -e " ${C_GREEN}${C_BOLD}4)${C_RESET} ${E_GEAR} $(_t "Octane Client Only")" - echo -e " ${C_GREEN}${C_BOLD}5)${C_RESET} ${E_GEAR} $(_t "Renderer Only")" - echo -e " ${C_GREEN}${C_BOLD}6)${C_RESET} ${E_GEAR} $(_t "Configs Only")" - echo -e " ${C_GREEN}${C_BOLD}19)${C_RESET} ${E_BOLT} $(_t "Dev Mode (skip builds, only configs)")" - echo "" - echo -e " ${C_BG_MAGENTA}${C_WHITE}${C_BOLD} $(_t "TOOLS") ${C_RESET}" - echo -e " ${C_MAGENTA}${C_BOLD}7)${C_RESET} ${E_GIT} $(_t "Switch Branch")" - echo -e " ${C_YELLOW}${C_BOLD}8)${C_RESET} ${E_DB} $(_t "Database")" - echo -e " ${C_YELLOW}${C_BOLD}9)${C_RESET} ${E_SHIELD} $(_t "Backup / Restore")" - echo -e " ${C_YELLOW}${C_BOLD}10)${C_RESET} ${E_WRENCH} $(_t "Services")" - echo -e " ${C_YELLOW}${C_BOLD}11)${C_RESET} ${E_CHART} $(_t "Status")" - echo -e " ${C_YELLOW}${C_BOLD}12)${C_RESET} ${E_HEART} $(_t "Health Check")" - echo -e " ${C_YELLOW}${C_BOLD}13)${C_RESET} ${E_BROOM} $(_t "Clean")" - echo -e " ${C_YELLOW}${C_BOLD}14)${C_RESET} ${E_EYE} $(_t "Logs")" - echo -e " ${C_YELLOW}${C_BOLD}15)${C_RESET} ${E_GEAR} $(_t "Config")" - echo "" - echo -e " ${C_BG_BLUE}${C_WHITE}${C_BOLD} $(_t "ADVANCED") ${C_RESET}" - echo -e " ${C_BLUE}${C_BOLD}16)${C_RESET} ${E_CONSOLE} $(_t "Git Log")" - echo -e " ${C_BLUE}${C_BOLD}17)${C_RESET} ${E_GLOBE} $(_t "Compare Branches")" - echo -e " ${C_BLUE}${C_BOLD}18)${C_RESET} ${E_CLOCK} $(_t "Cron Scheduler")" - echo -e " ${C_BLUE}${C_BOLD}20)${C_RESET} ${E_GIT} $(_t "View Git Stashes")" - echo -e " ${C_RED}${C_BOLD}21)${C_RESET} ${E_WRENCH} $(_t "Flyaway Repair")" - echo -e " ${C_GREEN}${C_BOLD}22)${C_RESET} ${E_ROCKET} $(_t "Auto-setup missing repos")" - echo -e " ${C_YELLOW}${C_BOLD}23)${C_RESET} ${E_BROOM} $(_t "Cleanup old Nitro dirs")" - echo -e " ${C_GREEN}${C_BOLD}24)${C_RESET} ${E_GLOBE} $(_t "Fix Client Links (icons/furniture/gamedata)")" - echo "" - echo -e " ${C_RED}${C_BOLD}0)${C_RESET} $(_t "Exit")" - echo "" - echo -en " ${C_CYAN}${C_BOLD}$(_t "Select [0-24]")${C_RESET}: " - local choice; read -rt 30 choice - - case "$choice" in - 1) SELECTIVE_UPDATE=""; (cmd_update) || warn "Update had errors" ;; - 2) SELECTIVE_UPDATE=""; (pick_branch_and_update) || warn "Update had errors" ;; - 3) SELECTIVE_UPDATE="emulator"; (pick_branch_and_update) || warn "Update had errors" ;; - 4) SELECTIVE_UPDATE="client"; (pick_branch_and_update) || warn "Update had errors" ;; - 5) SELECTIVE_UPDATE="renderer"; (pick_branch_and_update) || warn "Update had errors" ;; - 6) SELECTIVE_UPDATE="configs"; (pick_branch_and_update) || warn "Update had errors" ;; - 7) - echo "" - local i=1 - for b in "${branches[@]}"; do - local m=""; [ "$b" = "$NITRO_BRANCH" ] && m=" ${C_GREEN}*${C_RESET}" - echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $b$m"; i=$((i+1)) - done - echo -e " ${C_GREEN}${C_BOLD}$i)${C_RESET} $(_t "Custom")" - echo -en "\n $(_t "Branch") [1-$i]: "; local bc; read -rt 30 bc - if [[ "$bc" =~ ^[0-9]+$ ]] && [ "$bc" -ge 1 ] && [ "$bc" -lt "$i" ]; then - NITRO_BRANCH="${branches[$((bc-1))]}" - elif [ "$bc" = "$i" ]; then echo -en " $(_t "Name:") "; read -rt 30 NITRO_BRANCH - fi - load_branches branches - ok "Branch: %s" "$NITRO_BRANCH" ;; - 8) (cmd_database) ;; - 9) (cmd_backup); echo ""; (cmd_backups) ;; - 10) (cmd_services) ;; - 11) (cmd_status) ;; - 12) (cmd_health) ;; - 13) (cmd_clean) ;; - 14) (cmd_logs) ;; - 15) (cmd_config) ;; - 16) (cmd_gitlog) ;; - 17) (cmd_compare) ;; - 18) (cmd_schedule) ;; - 19) SELECTIVE_UPDATE="dev"; (pick_branch_and_update) || warn "Dev mode had errors" ;; - 20) (cmd_stash) ;; - 21) (cmd_flyaway_repair) ;; - 22) (cmd_setup) ;; - 23) (cmd_cleanup_old_nitro) ;; - 24) (cmd_fixlinks) ;; - 0|q|Q) echo -e "\n ${C_GREEN}$(_t "Goodbye!")${C_RESET}"; cursor_show; exit 0 ;; - *) warn "Invalid selection" ;; - esac - - echo "" - echo -en " ${C_DIM}$(_t "Press Enter to continue...")${C_RESET}" - read -rt 30 - done -} - -# ============================================================================= -# CLIENT LINK FIXER (URLs / icons / furniture / gamedata) -# ============================================================================= -# Resolve the public site URL the client configs must point at. Order: -# explicit NITRO_SITE_URL / --url > APP_URL > nginx server_name vhost. This -# lets the script self-configure on ANY hotel VPS without manual .env edits. -resolve_site_url() { - if [ -z "${NITRO_SITE_URL:-}" ] && [ -n "${APP_URL:-}" ]; then NITRO_SITE_URL="$APP_URL"; fi - if [ -z "${NITRO_SITE_URL:-}" ]; then - for _c in "${NGINX_SITE_CONFS[@]}"; do - [ -f "$_c" ] || continue - NITRO_SITE_URL=$(grep -oE 'server_name[^;]+;' "$_c" 2>/dev/null | head -1 \ - | tr -s ' ' '\n' | grep -vE 'server_name|;|127\.0\.0\.1|172\.|10\.|192\.168|localhost|\[?' | head -1) - [ -n "$NITRO_SITE_URL" ] && NITRO_SITE_URL="https://$NITRO_SITE_URL" && break - done - fi - NITRO_SITE_URL="${NITRO_SITE_URL%/}" - case "${NITRO_SITE_URL:-}" in - https://*) NITRO_WS_PROTO="wss://" ; NITRO_DOMAIN="${NITRO_SITE_URL#https://}" ;; - http://*) NITRO_WS_PROTO="ws://" ; NITRO_DOMAIN="${NITRO_SITE_URL#http://}" ;; - *) NITRO_WS_PROTO="wss://" ; NITRO_DOMAIN="${NITRO_SITE_URL:-}" ;; - esac -} - -# Derive every client-facing URL (icons, furniture/gamedata configs, bundled -# assets, websocket...) from the detected site. Explicit NITRO_*_URL values in -# .env win; everything else is computed and sanitized so the client never ends -# up pointed at /nitro-client/ or a path missing its trailing slash. -compute_nitro_urls() { - [ -n "${NITRO_SITE_URL:-}" ] || resolve_site_url - NITRO_IMAGE_LIBRARY_URL="${NITRO_IMAGE_LIBRARY_URL:-${NITRO_SITE_URL:-}/gamedata/c_images/}" - NITRO_HOF_FURNITURE_URL="${NITRO_HOF_FURNITURE_URL:-${NITRO_SITE_URL:-}/gamedata/icons}" - NITRO_API_URL="${NITRO_API_URL:-${NITRO_SITE_URL:-}}" - NITRO_SOCKET_URL="${NITRO_SOCKET_URL:-${NITRO_WS_PROTO}ws.${NITRO_DOMAIN}}" - NITRO_GAMEDATA_URL="${NITRO_GAMEDATA_URL:-${NITRO_SITE_URL:-}/gamedata}" - NITRO_ASSET_URL="${NITRO_ASSET_URL:-${NITRO_SITE_URL:-}/gamedata/bundled}" - NITRO_FURNI_ASSET_ICON_URL="${NITRO_FURNI_ASSET_ICON_URL:-${NITRO_SITE_URL:-}/gamedata/icons/%libname%%param%_icon.png}" - # Normalize slashes. - NITRO_API_URL="${NITRO_API_URL%/}" - NITRO_GAMEDATA_URL="${NITRO_GAMEDATA_URL%/}" - NITRO_ASSET_URL="${NITRO_ASSET_URL%/}" - NITRO_HOF_FURNITURE_URL="${NITRO_HOF_FURNITURE_URL%/}" - NITRO_FURNI_ASSET_ICON_URL="${NITRO_FURNI_ASSET_ICON_URL%/}" - NITRO_IMAGE_LIBRARY_URL="${NITRO_IMAGE_LIBRARY_URL%/}" - [ -n "$NITRO_IMAGE_LIBRARY_URL" ] && NITRO_IMAGE_LIBRARY_URL="$NITRO_IMAGE_LIBRARY_URL/" - # Never let these point at the client's own path. - case "$NITRO_IMAGE_LIBRARY_URL" in - *"/nitro-client"*) NITRO_IMAGE_LIBRARY_URL="${NITRO_SITE_URL:-}/gamedata/c_images/"; warn "Corrected NITRO_IMAGE_LIBRARY_URL (was /nitro-client/)" ;; - esac - case "$NITRO_GAMEDATA_URL" in - *"/nitro-client"*) NITRO_GAMEDATA_URL="${NITRO_SITE_URL:-}/gamedata"; warn "Corrected NITRO_GAMEDATA_URL (was /nitro-client/)" ;; - esac -} - -# HTTP answer code for a URL (000 = unreachable/curl failed). Never aborts the -# caller, even under `set -e` — a failed probe is reported as 000, not an error. -http_status() { - local code - code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 --max-redirs 4 -k -L "$1" 2>/dev/null) || code=000 - if [ -n "$code" ]; then echo "$code"; else echo 000; fi -} - -# Re-generate .gz sidecars for the heavy static assets so nginx `gzip_static` -# serves them instantly (no per-request compression). Also restores any loose -# original that was left behind as only the .gz sidecar — a state that would -# otherwise make the origin 404 on those files. -precompress_assets() { - local root f base - for root in "$(dirname "${GAMEDATA_CONF_DIR:-/var/www/Gamedata/config}")" "${NITRO_CLIENT:-/var/www/Octane}/dist"; do - [ -d "$root" ] || continue - while IFS= read -r f; do - base="${f%.gz}" - [ -f "$base" ] || { gunzip -k "$f" && warn "Restored missing original: %s" "$base"; } - done < <(find "$root" -name "*.gz" -type f 2>/dev/null) - find "$root" -type f \( -name "*.js" -o -name "*.css" -o -name "*.wasm" -o -name "*.json" -o -name "*.jsonc" \) -size +20k -print0 2>/dev/null \ - | while IFS= read -r -d '' f; do gzip -k -f -6 "$f"; done - done - ok "$(_t "Assets compressed for fast serving")" -} - -# TCP reachability probe for the websocket endpoint (curl cannot talk `wss://`). -check_socket() { - local url="$1" hostport host port - [ -n "$url" ] || { warn "$(_t "Socket not configured")"; bad_urls=$((bad_urls + 1)); return 0; } - hostport="${url#*://}"; hostport="${hostport%%/*}" - case "$hostport" in - *:*) host="${hostport%%:*}"; port="${hostport##*:}" ;; - *) host="$hostport"; port="443" ;; - esac - if timeout 5 bash -c "exec 3<>/dev/tcp/'$host'/'$port'" 2>/dev/null; then - ok "$(_t "Socket reachable") (%s:%s)" "$host" "$port" - ok_urls=$((ok_urls + 1)) - else - fail "$(_t "Socket unreachable") (%s:%s)" "$host" "$port" - bad_urls=$((bad_urls + 1)) - fi -} - -# Recompute + force-write every client-facing URL into renderer-config and -# ui-config (source, dist mirror and gamedata mirror), verify the on-disk asset -# directories exist and that the live URLs actually answer, then reload nginx -# and flush Redis so the client picks everything up. Self-heals stale or -# placeholder links (icons, furniture/gamedata, bundled assets, socket) on any -# system — crypto.* settings are still never touched. -cmd_fixlinks() { - acquire_lock - - step 1 5 "$(_t "Detect Site & URLs")" - echo "" - resolve_site_url - compute_nitro_urls - if [ -z "$NITRO_SITE_URL" ]; then - fail "$(_t "No site URL detected (set APP_URL or NITRO_SITE_URL)")" - release_lock - return 1 - fi - echo -e " ${C_CYAN}$(_t "Site:")${C_RESET} ${C_GREEN}${NITRO_SITE_URL}${C_RESET}" - echo -e " ${C_CYAN}$(_t "Socket:")${C_RESET} ${C_GREEN}${NITRO_SOCKET_URL}${C_RESET}" - echo -e " ${C_CYAN}$(_t "Gamedata:")${C_RESET} ${C_GREEN}${NITRO_GAMEDATA_URL}${C_RESET}" - echo -e " ${C_CYAN}$(_t "Images:")${C_RESET} ${C_GREEN}${NITRO_IMAGE_LIBRARY_URL}${C_RESET}" - - step 2 5 "$(_t "Verify on-disk asset paths")" - echo "" - local misses=0 - local gamedata_root; gamedata_root=$(dirname "$GAMEDATA_CONF_DIR") - for d in "$gamedata_root/c_images" "$gamedata_root/bundled" "$gamedata_root/icons" "$GAMEDATA_CONF_DIR"; do - if [ -d "$d" ]; then ok "%s" "$d"; else fail "Missing dir: %s" "$d"; misses=$((misses + 1)); fi - done - if [ -f "$GAMEDATA_CONF_DIR/FurnitureData.json" ]; then - ok "FurnitureData.json present" - else - fail "Missing: %s/FurnitureData.json" "$GAMEDATA_CONF_DIR" - misses=$((misses + 1)) - fi - if [ "$misses" -gt 0 ]; then warn "%s path(s) missing — some assets will 404" "$misses"; fi - - step 3 5 "$(_t "Write corrected links (forced)")" - echo "" - local dcd="$NITRO_CLIENT/dist/configuration" - mkdir -p "$dcd" "$NITRO_SRC_DIR" "$GAMEDATA_CONF_DIR" 2>/dev/null || true - if NITRO_SRC_DIR="$NITRO_SRC_DIR" NITRO_DIST_CONFIG_DIR="$dcd" NITRO_GAMEDATA_CONF_DIR="$GAMEDATA_CONF_DIR" \ - NITRO_IMAGE_LIBRARY_URL="$NITRO_IMAGE_LIBRARY_URL" NITRO_HOF_FURNITURE_URL="$NITRO_HOF_FURNITURE_URL" \ - NITRO_API_URL="$NITRO_API_URL" NITRO_SOCKET_URL="$NITRO_SOCKET_URL" \ - NITRO_GAMEDATA_URL="$NITRO_GAMEDATA_URL" NITRO_ASSET_URL="$NITRO_ASSET_URL" \ - NITRO_FURNI_ASSET_ICON_URL="$NITRO_FURNI_ASSET_ICON_URL" \ - NITRO_URL_FORCE=1 \ - node "$SCRIPT_DIR/scripts/sync-nitro-urls.cjs" 2>/dev/null; then - ok "$(_t "Links written to configs")" - else - fail "$(_t "Could not write links")" - fi - validate_configs "$NITRO_SRC_DIR" - - step 4 5 "$(_t "Validate links over HTTP")" - echo "" - if [ "$misses" -gt 0 ]; then warn "$(_t "Skipping some URL checks — on-disk paths missing")"; fi - local chk ok_urls=0 bad_urls=0 - check_url() { - local label="$1" url="$2" msg="" - [ -n "$url" ] || { return 0; } - chk=$(http_status "$url") - msg="${label}: ${url} (HTTP ${chk})" - case "$chk" in - 200) ok "%s" "$msg"; ok_urls=$((ok_urls + 1)) ;; - [23][0-9][0-9]|40[013]) ok "%s $(_t "reachable")" "$msg"; ok_urls=$((ok_urls + 1)) ;; - 404) fail "%s (404)" "$msg"; bad_urls=$((bad_urls + 1)) ;; - 000) fail "%s ($(_t "unreachable"))" "$msg"; bad_urls=$((bad_urls + 1)) ;; - *) warn "%s" "$msg"; bad_urls=$((bad_urls + 1)) ;; - esac - } - check_url "$(_t "Site")" "$NITRO_API_URL" - check_url "$(_t "FurnitureData")" "$NITRO_GAMEDATA_URL/config/FurnitureData.json" - check_url "$(_t "Images")" "${NITRO_IMAGE_LIBRARY_URL}reception/AIRpromo_background_left.png" - check_url "$(_t "Icons")" "${NITRO_HOF_FURNITURE_URL}/00011_icon.png" - check_url "$(_t "Bundled assets")" "$NITRO_ASSET_URL/figure/Hair_U_leet4.nitro" - check_socket "$NITRO_SOCKET_URL" - if [ "$ok_urls" -gt 0 ]; then ok "%s $(_t "URL(s) OK")" "$ok_urls"; fi - if [ "$bad_urls" -gt 0 ]; then warn "%s $(_t "URL(s) need attention")" "$bad_urls"; fi - - step 5 5 "$(_t "Apply (nginx + redis)")" - echo "" - ensure_gamedata_redirect - ensure_ws_proxy - if command -v nginx &>/dev/null && sudo nginx -t >/dev/null 2>&1; then - sudo nginx -s reload >/dev/null 2>&1 || sudo systemctl reload nginx >/dev/null 2>&1 || true - ok "Nginx reloaded" - fi - command -v redis-cli &>/dev/null && redis-cli FLUSHALL 2>/dev/null && ok "Redis flushed" || true - fix_perms - precompress_assets - info "$(_t "Done — links fixed. Test the client now.")" - release_lock - return 0 -} - -# ============================================================================= -# HELP -# ============================================================================= -show_help() { - echo "" - echo -e " ${C_GOLD}${C_BOLD}$(_t "SMaRT — Emulator / Octane / Octane-Renderer Updater & Manager")${C_RESET}" - echo -e " ${C_DIM}by Remco — epicnabbo.nl v${SCRIPT_VERSION} (build ${SCRIPT_BUILD})${C_RESET}" - echo "" - echo -e " ${C_BOLD}$(_t "USAGE:")${C_RESET} $SCRIPT_NAME [options]" - echo "" - echo -e " ${C_BOLD}$(_t "COMMANDS:")${C_RESET}" - echo -e " ${C_GREEN}interactive${C_RESET} $(_t "Open menu (default)")" - echo -e " ${C_GREEN}update${C_RESET} $(_t "Run update")" - echo -e " ${C_GREEN}backup${C_RESET} $(_t "Create database backup")" - echo -e " ${C_GREEN}restore${C_RESET} $(_t "Restore from backup")" - echo -e " ${C_GREEN}backups${C_RESET} $(_t "List backups")" - echo -e " ${C_GREEN}status${C_RESET} $(_t "System dashboard")" - echo -e " ${C_GREEN}health${C_RESET} $(_t "Health check")" - echo -e " ${C_GREEN}services${C_RESET} $(_t "Service manager")" - echo -e " ${C_GREEN}database${C_RESET} $(_t "Database tools")" - echo -e " ${C_GREEN}config${C_RESET} $(_t "Show config")" - echo -e " ${C_GREEN}logs${C_RESET} $(_t "View logs")" - echo -e " ${C_GREEN}clean${C_RESET} $(_t "Cache cleanup")" - echo -e " ${C_GREEN}gitlog${C_RESET} $(_t "Git history")" - echo -e " ${C_GREEN}compare${C_RESET} $(_t "Branch diff")" - echo -e " ${C_GREEN}schedule${C_RESET} $(_t "Cron setup")" - echo -e " ${C_GREEN}stash${C_RESET} $(_t "View git stashes")" - echo -e " ${C_GREEN}repair${C_RESET} $(_t "Flyaway Repair")" - echo -e " ${C_GREEN}setup${C_RESET} $(_t "Auto-setup missing repos")" - echo -e " ${C_GREEN}cleanup-nitro${C_RESET} $(_t "Cleanup old Nitro dirs")" - echo -e " ${C_GREEN}fixlinks${C_RESET} $(_t "Fix client links (icons/furniture/gamedata)")" - echo "" - echo -e " ${C_BOLD}$(_t "OPTIONS:")${C_RESET}" - echo -e " ${C_CYAN}--branch, -b${C_RESET} $(_t "Branch (auto-detects)")" - echo -e " ${C_CYAN}--url, -u${C_RESET} $(_t "Site URL")" - echo -e " ${C_CYAN}--only=${C_RESET} $(_t "emulator|client|renderer|configs|dev")" - echo -e " ${C_CYAN}--dry-run, -n${C_RESET} $(_t "Preview only (no changes)")" - echo -e " ${C_CYAN}--help, -h${C_RESET} $(_t "Show this help")" - echo -e " ${C_CYAN}--version, -V${C_RESET} $(_t "Show version")" - echo "" -} - -# ============================================================================= -# MAIN -# ============================================================================= -main() { - load_language - local cmd="" - while [ $# -gt 0 ]; do - case "$1" in - update|up) cmd="update" ;; - interactive|menu|ui) cmd="interactive" ;; - status|st) cmd="status" ;; - health|check) cmd="health" ;; - backup|bk) cmd="backup" ;; - restore|rs) cmd="restore" ;; - backups|bl) cmd="backups" ;; - gitlog|gl) cmd="gitlog" ;; - compare|diff) cmd="compare" ;; - schedule|cron) cmd="schedule" ;; - services|svc) cmd="services" ;; - database|db) cmd="database" ;; - config|cfg) cmd="config" ;; - logs|log) cmd="logs" ;; - clean|cl) cmd="clean" ;; - stash|stashes) cmd="stash" ;; - repair|flyaway) cmd="repair" ;; - setup|init) cmd="setup" ;; - cleanup-nitro|oldnitro) cmd="cleanup-nitro" ;; - fixlinks|fix-links|links) cmd="fixlinks" ;; - help|-h|--help) cmd="help" ;; - version|-V|--version) echo "v${SCRIPT_VERSION} (build ${SCRIPT_BUILD})"; exit 0 ;; - --branch|-b) shift; NITRO_BRANCH="$1" ;; - --url|-u) shift; NITRO_SITE_URL="$1" ;; - --only=*) SELECTIVE_UPDATE="${1#*=}" ;; - --dry-run|-n) DRY_RUN=true ;; - -*) echo "$(_t "Unknown:") $1"; exit 1 ;; - *) [ -z "$cmd" ] && cmd="interactive" ;; - esac - shift - done - [ -z "$cmd" ] && cmd="interactive" - - # Auto-detect the branch from the emulator repo when none was configured, - # so "Quick Update — current branch" actually updates the current branch. - if [ -z "${NITRO_BRANCH:-}" ]; then - NITRO_BRANCH="$(cd "$EMULATOR_REPO" 2>/dev/null && git branch --show-current 2>/dev/null || echo "main")" - [ -z "$NITRO_BRANCH" ] && NITRO_BRANCH="main" - info "Auto-detected branch: %s" "$NITRO_BRANCH" - fi - - resolve_site_url - compute_nitro_urls - - # Validate computed URLs for common misconfigurations. - _nitro_url_warn() { - local _key="$1" _val="$2" _hint="$3" - case "$_val" in - "") warn "$_key not set ($_hint)" ;; - */nitro-client/*|*/nitro-client) fail "$_key points at the Nitro client path. It must point to the gamedata/c_images directory." ;; - *://*/nitro-client/) fail "$_key points at the Nitro client URL. Set NITRO_IMAGE_LIBRARY_URL=/gamedata/c_images/ or correct NITRO_SITE_URL." ;; - esac - case "$_key" in - NITRO_IMAGE_LIBRARY_URL) - case "$_val" in - */c_images/) ok "$_key correctly includes /c_images/" ;; - *://*) warn "$_key does not end with /c_images/ — current: $_val" ;; - esac ;; - esac - } - _nitro_url_warn "NITRO_SITE_URL" "$NITRO_SITE_URL" "site URL" - _nitro_url_warn "NITRO_IMAGE_LIBRARY_URL" "$NITRO_IMAGE_LIBRARY_URL" "image library URL" - _nitro_url_warn "NITRO_GAMEDATA_URL" "$NITRO_GAMEDATA_URL" "gamedata URL" - - exec > >(tee -a "$LOG_FILE") 2>&1 - - case "$cmd" in - interactive) cmd_interactive ;; - update) cmd_update ;; - backup) cmd_backup ;; - restore) cmd_restore ;; - backups) cmd_backups ;; - status) cmd_status ;; - health) cmd_health ;; - services) cmd_services ;; - database) cmd_database ;; - config) cmd_config ;; - logs) cmd_logs ;; - clean) cmd_clean ;; - gitlog) cmd_gitlog ;; - compare) cmd_compare ;; - schedule) cmd_schedule ;; - stash) cmd_stash ;; - repair) cmd_flyaway_repair ;; - setup) cmd_setup ;; - cleanup-nitro) cmd_cleanup_old_nitro ;; - fixlinks) cmd_fixlinks ;; - help) show_help ;; - esac - - cursor_show -} - -main "$@"