dubs-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +280 -0
- package/CLAUDE.md +46 -0
- package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
- package/CURRENT_SESSION.md +171 -0
- package/CURRENT_SESSION_DRAW.md +516 -0
- package/MARCH_MADNESS_SURVIVOR.md +254 -0
- package/PANDA.md +166 -0
- package/Procfile +4 -0
- package/README.md +476 -0
- package/controllers/livescoresController.js +376 -0
- package/controllers/pickemController.js +554 -0
- package/controllers/survivorAdminController.js +887 -0
- package/controllers/survivorController.js +623 -0
- package/cron/oracleMonitor.js +77 -0
- package/cron/pickemOracleMonitor.js +73 -0
- package/data/jackpot-history.json +952 -0
- package/data/ncaaTeams.js +406 -0
- package/documentation/API_SECURITY_GUIDE.md +327 -0
- package/documentation/ARCADE_API.md +593 -0
- package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
- package/documentation/ARCADE_QUICKSTART.md +242 -0
- package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
- package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
- package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
- package/documentation/CLAIM_STATUS_FIX.md +67 -0
- package/documentation/CLI_TOOL_GUIDE.md +372 -0
- package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
- package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
- package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
- package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
- package/documentation/DATABASE_QUERIES.md +269 -0
- package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
- package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
- package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
- package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
- package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
- package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
- package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
- package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
- package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
- package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
- package/documentation/HEROKU_DEPLOYMENT.md +134 -0
- package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
- package/documentation/JACKPOT_API.md +521 -0
- package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
- package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
- package/documentation/JWT_QUICK_SETUP.md +268 -0
- package/documentation/JWT_TESTING_GUIDE.md +404 -0
- package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
- package/documentation/KEEPER_SETUP.md +206 -0
- package/documentation/KEEPER_STATE_MACHINE.md +423 -0
- package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
- package/documentation/LOCAL_VOTING_TEST.md +279 -0
- package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
- package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
- package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
- package/documentation/PNL_TRACKING_SETUP.md +189 -0
- package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
- package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
- package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
- package/documentation/QUICK_AUTH_SETUP.md +99 -0
- package/documentation/QUICK_DEPLOY.md +224 -0
- package/documentation/QUICK_FIX.md +114 -0
- package/documentation/QUICK_START.md +152 -0
- package/documentation/REFEREE_MODE_GUIDE.md +392 -0
- package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
- package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
- package/documentation/RUN_MIGRATION_NOW.md +39 -0
- package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
- package/documentation/SETUP_GUIDE.md +184 -0
- package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
- package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
- package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
- package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
- package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
- package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
- package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
- package/ecosystem.config.js +65 -0
- package/env.template +125 -0
- package/middleware/apiKeyAuth.js +136 -0
- package/middleware/authenticate.js +214 -0
- package/middleware/developerUserAuth.js +76 -0
- package/middleware/socketAuth.js +69 -0
- package/package.json +49 -0
- package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
- package/postman/Dubs-API-v1.postman_collection.json +205 -0
- package/postman/Dubs_Developer_API.postman_collection.json +662 -0
- package/postman/QUICKSTART.md +118 -0
- package/postman/QUICK_REFERENCE.md +246 -0
- package/postman/README.md +71 -0
- package/postman/VOTING_API_GUIDE.md +426 -0
- package/refactor/Animations.md +148 -0
- package/refactor/Chat.md +252 -0
- package/routes/actionsRoutes.js +699 -0
- package/routes/adminRoutes.js +370 -0
- package/routes/analyticsRoutes.js +1262 -0
- package/routes/arcadeRoutes.js +557 -0
- package/routes/authRoutes.js +2310 -0
- package/routes/avatarRoutes.js +85 -0
- package/routes/botRoutes.js +211 -0
- package/routes/chatRoutes.js +377 -0
- package/routes/cryptoPriceRoutes.js +105 -0
- package/routes/developerRoutes.js +4201 -0
- package/routes/deviceRoutes.js +214 -0
- package/routes/dmRoutes.js +167 -0
- package/routes/esportsRoutes.js +806 -0
- package/routes/exchangeRateRoutes.js +233 -0
- package/routes/gamesRoutes.js +3028 -0
- package/routes/jackpotRoutes.js +754 -0
- package/routes/keeperMonitoringRoutes.js +156 -0
- package/routes/keeperWebhookRoutes.js +466 -0
- package/routes/livescoresRoutes.js +31 -0
- package/routes/pickemAdminRoutes.js +199 -0
- package/routes/pickemRoutes.js +231 -0
- package/routes/playerStatsRoutes.js +147 -0
- package/routes/portfolioRoutes.js +217 -0
- package/routes/promoRoutes.js +418 -0
- package/routes/referralEarningsRoutes.js +392 -0
- package/routes/socialRoutes.js +459 -0
- package/routes/sportsRoutes.js +1271 -0
- package/routes/survivorAdminRoutes.js +345 -0
- package/routes/survivorRoutes.js +756 -0
- package/routes/uploadRoutes.js +256 -0
- package/routes/userProfileRoutes.js +244 -0
- package/routes/whatsNewRoutes.js +331 -0
- package/scripts/.claude/settings.local.json +15 -0
- package/scripts/README.md +170 -0
- package/scripts/RESTART_EVERYTHING.sh +104 -0
- package/scripts/add-claim-columns.sql +48 -0
- package/scripts/add-crypto-prices-cache.sql +27 -0
- package/scripts/add-exchange-rates-cache.sql +40 -0
- package/scripts/add-game-invite-column.sql +23 -0
- package/scripts/add-game-invite-notification.sql +33 -0
- package/scripts/add-game-invite-telegram-pref.sql +16 -0
- package/scripts/add-game-joined-notification.sql +16 -0
- package/scripts/add-game-joined-pref.js +40 -0
- package/scripts/add-game-joined-preference.sql +6 -0
- package/scripts/add-game-start-notifications.sql +41 -0
- package/scripts/add-notification-flags-to-games.sql +55 -0
- package/scripts/add-pending-game-dismissals.sql +19 -0
- package/scripts/add-preferred-currency.sql +34 -0
- package/scripts/add-winner-columns.js +61 -0
- package/scripts/add_mention_system.sql +53 -0
- package/scripts/add_payment_system.sql +96 -0
- package/scripts/add_sports_event_id_column.sql +22 -0
- package/scripts/analyze-cohort-data-heroku.js +276 -0
- package/scripts/analyze-cohort-data.js +295 -0
- package/scripts/analyze-prod-cohorts.sh +10 -0
- package/scripts/backfill-matchup-images.js +245 -0
- package/scripts/backfill-missing-signatures.js +175 -0
- package/scripts/backfill-referral-earnings.js +202 -0
- package/scripts/check-chat-schema.js +130 -0
- package/scripts/check-db.sh +14 -0
- package/scripts/check_oracle_in_game.js +54 -0
- package/scripts/cleanup-database.js +193 -0
- package/scripts/clear-notification-cache.js +85 -0
- package/scripts/convert-mnemonic.js +50 -0
- package/scripts/create-users-table.sql +44 -0
- package/scripts/debug-cohort-counts.js +248 -0
- package/scripts/debug-winner-calc.js +84 -0
- package/scripts/deploy-payment-system.sh +118 -0
- package/scripts/deploy-to-heroku.sh +63 -0
- package/scripts/diagnose-locked-round.js +143 -0
- package/scripts/dubs-cli.js +720 -0
- package/scripts/dump-account.js +65 -0
- package/scripts/find-vrf-offset.js +48 -0
- package/scripts/fix-chat-notifications-constraint.sql +122 -0
- package/scripts/fix-claim-columns.js +124 -0
- package/scripts/fix-constraint-now.js +44 -0
- package/scripts/fix-lock-timestamps.js +96 -0
- package/scripts/fix-locked-round.sh +126 -0
- package/scripts/fix-missing-badges.sql +91 -0
- package/scripts/fix-payment-notifications.sql +41 -0
- package/scripts/force-new-round.js +55 -0
- package/scripts/force-resolve-and-claim.js +278 -0
- package/scripts/important/README.md +115 -0
- package/scripts/important/authority-force-lock.js +197 -0
- package/scripts/important/authority-resolve-game.js +267 -0
- package/scripts/important/check-game-status.js +373 -0
- package/scripts/important/list-pending-games-by-version.js +270 -0
- package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
- package/scripts/initialize-jackpot.js +111 -0
- package/scripts/jackpot/.claude/settings.local.json +10 -0
- package/scripts/jackpot/force-reset.js +84 -0
- package/scripts/jackpot/initialize-mainnet.js +100 -0
- package/scripts/jackpot/keeper.js +742 -0
- package/scripts/jackpot/status.js +107 -0
- package/scripts/jackpot/update-round-duration.js +143 -0
- package/scripts/keeper-bot.js +112 -0
- package/scripts/list-pending-games.js +131 -0
- package/scripts/migrate-chat-v2.js +127 -0
- package/scripts/migrate-chat-winners.js +84 -0
- package/scripts/migrate-chat.sh +17 -0
- package/scripts/migrate-game-invite.js +83 -0
- package/scripts/migrate-heroku-game-notifications.sh +159 -0
- package/scripts/migrations/001_analytics_tables.sql +422 -0
- package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
- package/scripts/migrations/003_referral_earnings.sql +208 -0
- package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
- package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
- package/scripts/migrations/005_push_notifications.sql +55 -0
- package/scripts/migrations/006_add_draw_team_players.sql +28 -0
- package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
- package/scripts/migrations/007_add_gif_url.sql +8 -0
- package/scripts/migrations/008_add_connect4_columns.sql +139 -0
- package/scripts/migrations/008_add_pool_tracking.sql +22 -0
- package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
- package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
- package/scripts/migrations/011_create_developer_tables.sql +67 -0
- package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
- package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
- package/scripts/migrations/013_add_network_mode.sql +18 -0
- package/scripts/migrations/014_create_developer_app_users.sql +19 -0
- package/scripts/migrations/015_add_ui_config.sql +4 -0
- package/scripts/migrations/016_add_resolution_secret.sql +4 -0
- package/scripts/migrations/017_add_external_game_id.sql +3 -0
- package/scripts/migrations/018_create_pickem_tables.sql +115 -0
- package/scripts/migrations/019_expo_push_tokens.sql +19 -0
- package/scripts/migrations/create_whats_new_tables.sql +88 -0
- package/scripts/migrations/drop_live_games_tables.sql +34 -0
- package/scripts/open-jackpot-round.js +85 -0
- package/scripts/purge-all-data.sh +329 -0
- package/scripts/purge-all-data.sql +142 -0
- package/scripts/purge-heroku-data.sh +149 -0
- package/scripts/purge-heroku-data.sql +62 -0
- package/scripts/rebuild-heroku-database.sh +113 -0
- package/scripts/recover-funds.js +357 -0
- package/scripts/regenerate-epl-images.js +278 -0
- package/scripts/resize-s3-matchup-images.js +374 -0
- package/scripts/resolve-direct.js +88 -0
- package/scripts/resolve-mock-game.js +124 -0
- package/scripts/resolve-pickem-game.js +55 -0
- package/scripts/resolve-round-manual.js +83 -0
- package/scripts/resolve-stuck-game.js +382 -0
- package/scripts/resolve-stuck-round.js +42 -0
- package/scripts/run-connect4-migration.sh +16 -0
- package/scripts/run-mention-migration.sh +32 -0
- package/scripts/run-payment-migration.sh +51 -0
- package/scripts/run-preferred-currency-migration.sh +31 -0
- package/scripts/run-referral-earnings-migration.sh +32 -0
- package/scripts/run-survivor-outcome-migration.sh +16 -0
- package/scripts/seed-test-users.js +346 -0
- package/scripts/setup-auth-tables.js +78 -0
- package/scripts/setup-complete-database.sql +992 -0
- package/scripts/setup-database-fresh.sh +359 -0
- package/scripts/setup-heroku-keeper.sh +48 -0
- package/scripts/setup-keeper-database.js +83 -0
- package/scripts/setup-keeper-state-db.sql +110 -0
- package/scripts/setup-oracle.sh +39 -0
- package/scripts/setup-pnl-tracking.js +111 -0
- package/scripts/start-devnet.sh +14 -0
- package/scripts/test-arcade-devnet.sh +160 -0
- package/scripts/test-arcade-match.sh +109 -0
- package/scripts/test-automatic-mode.sh +239 -0
- package/scripts/test-connect4-cancel-claim.js +370 -0
- package/scripts/test-connect4-e2e.js +369 -0
- package/scripts/test-connect4-resolve.js +369 -0
- package/scripts/test-game-state-endpoint.js +136 -0
- package/scripts/test-invite-notification.js +86 -0
- package/scripts/test-jackpot-api.sh +71 -0
- package/scripts/test-poll-confirmation.js +267 -0
- package/scripts/test-resolve-game.js +271 -0
- package/scripts/test-resolve-signature.js +223 -0
- package/scripts/test-signature-preservation.js +124 -0
- package/scripts/test-state-machine.js +291 -0
- package/scripts/test-webhook-receiver.js +60 -0
- package/scripts/update-notification-constraint.js +52 -0
- package/scripts/verify-account-layout.js +145 -0
- package/scripts/verify-winner-algorithm.js +278 -0
- package/server.js +5259 -0
- package/services/arcadeMatchService.js +763 -0
- package/services/automaticGameOracle.js +1596 -0
- package/services/chatService.js +1612 -0
- package/services/connect4GameService.js +1049 -0
- package/services/connect4NotificationService.js +374 -0
- package/services/cryptoPriceService.js +223 -0
- package/services/customGameResolver.js +260 -0
- package/services/db.js +79 -0
- package/services/directMessageService.js +389 -0
- package/services/discordNotifications.js +160 -0
- package/services/exchangeRateService.js +289 -0
- package/services/expoPushService.js +314 -0
- package/services/gamesCacheService.js +539 -0
- package/services/jackpotHistory.js +331 -0
- package/services/jackpotService.js +856 -0
- package/services/keeperStateService.js +355 -0
- package/services/matchupImageService.js +591 -0
- package/services/notificationCacheService.js +407 -0
- package/services/pickemOracle.js +440 -0
- package/services/playerStatsService.js +389 -0
- package/services/portfolioService.js +555 -0
- package/services/promoService.js +757 -0
- package/services/promoTreasuryService.js +239 -0
- package/services/pushNotifications.js +353 -0
- package/services/redisService.js +422 -0
- package/services/referralEarningsService.js +728 -0
- package/services/s3Service.js +396 -0
- package/services/socialService.js +1202 -0
- package/services/survivorOracle.js +469 -0
- package/services/survivorSimulator.js +475 -0
- package/services/telegramNotifications.js +461 -0
- package/services/userProfileStatsService.js +1185 -0
- package/services/whatsNewService.js +388 -0
- package/utils/urlHelper.js +95 -0
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
-- ============================================
|
|
2
|
+
-- 🎮 DUBS SERVER - COMPLETE DATABASE SCHEMA
|
|
3
|
+
-- ============================================
|
|
4
|
+
-- This script creates ALL tables needed for the DUBS server
|
|
5
|
+
-- Safe to run multiple times (uses IF NOT EXISTS)
|
|
6
|
+
-- Run with: psql -d dubs_db -f scripts/setup-complete-database.sql
|
|
7
|
+
|
|
8
|
+
-- ============================================
|
|
9
|
+
-- 1. AUTHENTICATION & USERS
|
|
10
|
+
-- ============================================
|
|
11
|
+
|
|
12
|
+
-- Users table (main user profiles)
|
|
13
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
14
|
+
id SERIAL PRIMARY KEY,
|
|
15
|
+
wallet_address VARCHAR(44) UNIQUE NOT NULL,
|
|
16
|
+
email VARCHAR(255),
|
|
17
|
+
username VARCHAR(50) NOT NULL,
|
|
18
|
+
avatar TEXT,
|
|
19
|
+
referral_code VARCHAR(50),
|
|
20
|
+
my_referral_code VARCHAR(50),
|
|
21
|
+
signature TEXT,
|
|
22
|
+
onboarding_complete BOOLEAN DEFAULT false,
|
|
23
|
+
telegram_user_id BIGINT UNIQUE,
|
|
24
|
+
telegram_username VARCHAR(255),
|
|
25
|
+
telegram_first_name VARCHAR(255),
|
|
26
|
+
telegram_last_name VARCHAR(255),
|
|
27
|
+
telegram_photo_url TEXT,
|
|
28
|
+
telegram_connected_at TIMESTAMP,
|
|
29
|
+
preferred_currency VARCHAR(3) DEFAULT 'USD',
|
|
30
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
31
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
-- Auth nonces (for signature verification)
|
|
35
|
+
CREATE TABLE IF NOT EXISTS auth_nonces (
|
|
36
|
+
wallet_address VARCHAR(44) PRIMARY KEY,
|
|
37
|
+
nonce VARCHAR(64) NOT NULL,
|
|
38
|
+
expires_at TIMESTAMP NOT NULL,
|
|
39
|
+
used BOOLEAN DEFAULT false,
|
|
40
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
-- User sessions (JWT token management)
|
|
44
|
+
CREATE TABLE IF NOT EXISTS user_sessions (
|
|
45
|
+
id SERIAL PRIMARY KEY,
|
|
46
|
+
wallet_address VARCHAR(100) NOT NULL,
|
|
47
|
+
user_id INTEGER,
|
|
48
|
+
token_hash VARCHAR(64) NOT NULL,
|
|
49
|
+
expires_at TIMESTAMP NOT NULL,
|
|
50
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
51
|
+
last_activity TIMESTAMP DEFAULT NOW()
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
-- User badges (achievements)
|
|
55
|
+
CREATE TABLE IF NOT EXISTS user_badges (
|
|
56
|
+
id SERIAL PRIMARY KEY,
|
|
57
|
+
user_id INTEGER NOT NULL,
|
|
58
|
+
badge_type VARCHAR(50) NOT NULL,
|
|
59
|
+
badge_name VARCHAR(100) NOT NULL,
|
|
60
|
+
badge_description TEXT,
|
|
61
|
+
badge_icon TEXT,
|
|
62
|
+
referral_count INTEGER,
|
|
63
|
+
earned_at TIMESTAMP DEFAULT NOW(),
|
|
64
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
65
|
+
UNIQUE(user_id, badge_type)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
-- Telegram notification preferences
|
|
69
|
+
CREATE TABLE IF NOT EXISTS telegram_notification_preferences (
|
|
70
|
+
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
71
|
+
notify_reply BOOLEAN DEFAULT true,
|
|
72
|
+
notify_reaction BOOLEAN DEFAULT true,
|
|
73
|
+
notify_friend_request BOOLEAN DEFAULT true,
|
|
74
|
+
notify_friend_request_accepted BOOLEAN DEFAULT true,
|
|
75
|
+
notify_friend_request_declined BOOLEAN DEFAULT true,
|
|
76
|
+
notify_referral BOOLEAN DEFAULT true,
|
|
77
|
+
notify_mention BOOLEAN DEFAULT true,
|
|
78
|
+
notify_friend_message BOOLEAN DEFAULT true,
|
|
79
|
+
notify_game_joined BOOLEAN DEFAULT true,
|
|
80
|
+
notify_game_invite BOOLEAN DEFAULT true,
|
|
81
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
82
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
-- Push notification subscriptions (for PWA/seeker mode)
|
|
86
|
+
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
|
87
|
+
id SERIAL PRIMARY KEY,
|
|
88
|
+
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
89
|
+
endpoint TEXT NOT NULL,
|
|
90
|
+
p256dh TEXT NOT NULL,
|
|
91
|
+
auth TEXT NOT NULL,
|
|
92
|
+
device_type VARCHAR(50) DEFAULT 'android_pwa',
|
|
93
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
94
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
95
|
+
UNIQUE(user_id, endpoint)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
-- Push notification preferences (mirrors telegram_notification_preferences)
|
|
99
|
+
CREATE TABLE IF NOT EXISTS push_notification_preferences (
|
|
100
|
+
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
101
|
+
notify_reply BOOLEAN DEFAULT true,
|
|
102
|
+
notify_reaction BOOLEAN DEFAULT true,
|
|
103
|
+
notify_friend_request BOOLEAN DEFAULT true,
|
|
104
|
+
notify_friend_request_accepted BOOLEAN DEFAULT true,
|
|
105
|
+
notify_friend_request_declined BOOLEAN DEFAULT true,
|
|
106
|
+
notify_referral BOOLEAN DEFAULT true,
|
|
107
|
+
notify_mention BOOLEAN DEFAULT true,
|
|
108
|
+
notify_friend_message BOOLEAN DEFAULT true,
|
|
109
|
+
notify_game_joined BOOLEAN DEFAULT true,
|
|
110
|
+
notify_game_invite BOOLEAN DEFAULT true,
|
|
111
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
112
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
-- ============================================
|
|
116
|
+
-- 2. CHAT SYSTEM
|
|
117
|
+
-- ============================================
|
|
118
|
+
|
|
119
|
+
-- Chat messages (main chat table)
|
|
120
|
+
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
121
|
+
id SERIAL PRIMARY KEY,
|
|
122
|
+
user_id INTEGER,
|
|
123
|
+
wallet_address VARCHAR(100) NOT NULL,
|
|
124
|
+
username VARCHAR(100),
|
|
125
|
+
avatar TEXT,
|
|
126
|
+
message TEXT NOT NULL,
|
|
127
|
+
reply_to_id INTEGER,
|
|
128
|
+
is_winner_announcement BOOLEAN DEFAULT FALSE,
|
|
129
|
+
win_amount NUMERIC(20, 9),
|
|
130
|
+
round_id INTEGER,
|
|
131
|
+
game_invite_metadata JSONB,
|
|
132
|
+
edited BOOLEAN DEFAULT FALSE,
|
|
133
|
+
edited_at TIMESTAMP,
|
|
134
|
+
deleted BOOLEAN DEFAULT FALSE,
|
|
135
|
+
deleted_at TIMESTAMP,
|
|
136
|
+
timestamp TIMESTAMP DEFAULT NOW(),
|
|
137
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
-- Chat reactions (emojis on messages)
|
|
141
|
+
CREATE TABLE IF NOT EXISTS chat_reactions (
|
|
142
|
+
id SERIAL PRIMARY KEY,
|
|
143
|
+
message_id INTEGER,
|
|
144
|
+
user_id INTEGER,
|
|
145
|
+
wallet_address VARCHAR(100) NOT NULL,
|
|
146
|
+
reaction VARCHAR(20) NOT NULL,
|
|
147
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
148
|
+
UNIQUE(message_id, user_id, reaction)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
-- User relationships (friends/blocks)
|
|
152
|
+
CREATE TABLE IF NOT EXISTS user_relationships (
|
|
153
|
+
id SERIAL PRIMARY KEY,
|
|
154
|
+
user_id INTEGER,
|
|
155
|
+
target_user_id INTEGER,
|
|
156
|
+
relationship_type VARCHAR(20) NOT NULL CHECK (relationship_type IN ('friend', 'block')),
|
|
157
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
158
|
+
UNIQUE(user_id, target_user_id)
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
-- Chat notifications
|
|
162
|
+
CREATE TABLE IF NOT EXISTS chat_notifications (
|
|
163
|
+
id SERIAL PRIMARY KEY,
|
|
164
|
+
user_id INTEGER,
|
|
165
|
+
message_id INTEGER,
|
|
166
|
+
sender_user_id INTEGER,
|
|
167
|
+
notification_type VARCHAR(30) NOT NULL CHECK (notification_type IN ('reply', 'mention', 'friend_message', 'reaction', 'friend_request', 'friend_request_accepted', 'friend_request_declined', 'referral', 'game_joined', 'game_invite', 'game_starting_soon', 'game_starting_now', 'game_won', 'game_lost', 'payment_received', 'payment_sent', 'dm', 'dm_message', 'whats_new')),
|
|
168
|
+
notification_data JSONB,
|
|
169
|
+
read BOOLEAN DEFAULT FALSE,
|
|
170
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
-- Message mentions (@mention system)
|
|
174
|
+
CREATE TABLE IF NOT EXISTS message_mentions (
|
|
175
|
+
id SERIAL PRIMARY KEY,
|
|
176
|
+
message_id INTEGER NOT NULL REFERENCES chat_messages(id) ON DELETE CASCADE,
|
|
177
|
+
mentioned_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
178
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
179
|
+
UNIQUE(message_id, mentioned_user_id)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
-- Chat payments (SOL payment tracking)
|
|
183
|
+
CREATE TABLE IF NOT EXISTS chat_payments (
|
|
184
|
+
id SERIAL PRIMARY KEY,
|
|
185
|
+
message_id INTEGER REFERENCES chat_messages(id) ON DELETE CASCADE,
|
|
186
|
+
sender_user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
187
|
+
sender_wallet VARCHAR(100) NOT NULL,
|
|
188
|
+
recipient_user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
189
|
+
recipient_wallet VARCHAR(100) NOT NULL,
|
|
190
|
+
amount_sol NUMERIC(20, 9) NOT NULL,
|
|
191
|
+
transaction_signature VARCHAR(200) NOT NULL UNIQUE,
|
|
192
|
+
status VARCHAR(20) NOT NULL CHECK (status IN ('pending', 'confirmed', 'failed')),
|
|
193
|
+
error_message TEXT,
|
|
194
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
195
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
-- ============================================
|
|
199
|
+
-- 3. GAMES SYSTEM (Sports Betting)
|
|
200
|
+
-- ============================================
|
|
201
|
+
|
|
202
|
+
-- Games table (shared game data)
|
|
203
|
+
CREATE TABLE IF NOT EXISTS games (
|
|
204
|
+
id SERIAL PRIMARY KEY,
|
|
205
|
+
game_id VARCHAR(255) UNIQUE NOT NULL,
|
|
206
|
+
game_address VARCHAR(255) NOT NULL,
|
|
207
|
+
title VARCHAR(500),
|
|
208
|
+
image_url TEXT,
|
|
209
|
+
matchup_image_url TEXT, -- Pre-generated S3 matchup image URL
|
|
210
|
+
game_type VARCHAR(50),
|
|
211
|
+
buy_in NUMERIC(20, 9),
|
|
212
|
+
max_players INTEGER DEFAULT 0,
|
|
213
|
+
game_mode INTEGER,
|
|
214
|
+
created_by VARCHAR(255) NOT NULL,
|
|
215
|
+
sports_event JSONB,
|
|
216
|
+
home_team_players TEXT[] DEFAULT '{}',
|
|
217
|
+
away_team_players TEXT[] DEFAULT '{}',
|
|
218
|
+
draw_team_players TEXT[] DEFAULT '{}', -- For EPL/soccer draw betting
|
|
219
|
+
lock_timestamp BIGINT,
|
|
220
|
+
is_locked BOOLEAN DEFAULT false,
|
|
221
|
+
is_resolved BOOLEAN DEFAULT false,
|
|
222
|
+
automatic_status VARCHAR(50),
|
|
223
|
+
lock_notification_sent_10min BOOLEAN DEFAULT false,
|
|
224
|
+
lock_notification_sent_now BOOLEAN DEFAULT false,
|
|
225
|
+
-- Connect4 specific columns
|
|
226
|
+
game_status VARCHAR(50), -- waiting, playing, completed, cancelled
|
|
227
|
+
connect4_board JSONB, -- 6x7 2D array board state
|
|
228
|
+
connect4_current_turn VARCHAR(10), -- home or away
|
|
229
|
+
connect4_winner VARCHAR(10), -- home, away, draw, or NULL
|
|
230
|
+
connect4_winning_cells JSONB, -- Array of {row, col} winning cells
|
|
231
|
+
claim_signature TEXT, -- On-chain claim transaction signature
|
|
232
|
+
completed_at TIMESTAMP, -- When game ended
|
|
233
|
+
invited_player VARCHAR(255), -- Wallet for private game invites
|
|
234
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
235
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
-- User game refs table (user-specific game data)
|
|
239
|
+
CREATE TABLE IF NOT EXISTS user_game_refs (
|
|
240
|
+
id SERIAL PRIMARY KEY,
|
|
241
|
+
wallet_address VARCHAR(255) NOT NULL,
|
|
242
|
+
game_id VARCHAR(255) NOT NULL,
|
|
243
|
+
role VARCHAR(50),
|
|
244
|
+
joined_at TIMESTAMP,
|
|
245
|
+
team_choice VARCHAR(10),
|
|
246
|
+
my_signature VARCHAR(255),
|
|
247
|
+
my_explorer_url TEXT,
|
|
248
|
+
status VARCHAR(50),
|
|
249
|
+
wallet_type VARCHAR(50),
|
|
250
|
+
claimed_at TIMESTAMP,
|
|
251
|
+
claim_signature TEXT,
|
|
252
|
+
claim_explorer_url TEXT,
|
|
253
|
+
amount_claimed NUMERIC(20, 9),
|
|
254
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
255
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
256
|
+
UNIQUE(wallet_address, game_id)
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
-- Audit logs table
|
|
260
|
+
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
261
|
+
id SERIAL PRIMARY KEY,
|
|
262
|
+
log_type VARCHAR(100),
|
|
263
|
+
method VARCHAR(100),
|
|
264
|
+
user_id VARCHAR(255),
|
|
265
|
+
metadata JSONB,
|
|
266
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
-- ============================================
|
|
270
|
+
-- 4. SOCIAL FEATURES
|
|
271
|
+
-- ============================================
|
|
272
|
+
|
|
273
|
+
-- Friends table
|
|
274
|
+
CREATE TABLE IF NOT EXISTS friends (
|
|
275
|
+
id SERIAL PRIMARY KEY,
|
|
276
|
+
user_wallet VARCHAR(100) NOT NULL,
|
|
277
|
+
friend_wallet VARCHAR(100) NOT NULL,
|
|
278
|
+
status VARCHAR(20) DEFAULT 'pending',
|
|
279
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
280
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
281
|
+
UNIQUE(user_wallet, friend_wallet)
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
-- Friend requests
|
|
285
|
+
CREATE TABLE IF NOT EXISTS friend_requests (
|
|
286
|
+
id SERIAL PRIMARY KEY,
|
|
287
|
+
from_user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
288
|
+
to_user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
289
|
+
status VARCHAR(20) NOT NULL CHECK (status IN ('pending', 'accepted', 'rejected')) DEFAULT 'pending',
|
|
290
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
291
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
-- Add unique constraint for friend_requests if it doesn't exist
|
|
295
|
+
DO $$
|
|
296
|
+
BEGIN
|
|
297
|
+
IF NOT EXISTS (
|
|
298
|
+
SELECT 1 FROM pg_constraint
|
|
299
|
+
WHERE conname = 'friend_requests_from_user_id_to_user_id_key'
|
|
300
|
+
) THEN
|
|
301
|
+
ALTER TABLE friend_requests
|
|
302
|
+
ADD CONSTRAINT friend_requests_from_user_id_to_user_id_key
|
|
303
|
+
UNIQUE (from_user_id, to_user_id);
|
|
304
|
+
END IF;
|
|
305
|
+
END $$;
|
|
306
|
+
|
|
307
|
+
-- Groups (group chats)
|
|
308
|
+
CREATE TABLE IF NOT EXISTS groups (
|
|
309
|
+
id SERIAL PRIMARY KEY,
|
|
310
|
+
name VARCHAR(100) NOT NULL,
|
|
311
|
+
description TEXT,
|
|
312
|
+
creator_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
313
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
-- Group members
|
|
317
|
+
CREATE TABLE IF NOT EXISTS group_members (
|
|
318
|
+
id SERIAL PRIMARY KEY,
|
|
319
|
+
group_id INTEGER REFERENCES groups(id) ON DELETE CASCADE,
|
|
320
|
+
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
|
321
|
+
role VARCHAR(20) DEFAULT 'member',
|
|
322
|
+
joined_at TIMESTAMP DEFAULT NOW()
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
-- Add unique constraint for group_members if it doesn't exist
|
|
326
|
+
DO $$
|
|
327
|
+
BEGIN
|
|
328
|
+
IF NOT EXISTS (
|
|
329
|
+
SELECT 1 FROM pg_constraint
|
|
330
|
+
WHERE conname = 'group_members_group_id_user_id_key'
|
|
331
|
+
) THEN
|
|
332
|
+
ALTER TABLE group_members
|
|
333
|
+
ADD CONSTRAINT group_members_group_id_user_id_key
|
|
334
|
+
UNIQUE (group_id, user_id);
|
|
335
|
+
END IF;
|
|
336
|
+
END $$;
|
|
337
|
+
|
|
338
|
+
-- ============================================
|
|
339
|
+
-- 5. PLAYER STATS & TRACKING
|
|
340
|
+
-- ============================================
|
|
341
|
+
|
|
342
|
+
-- Player stats (overall performance)
|
|
343
|
+
CREATE TABLE IF NOT EXISTS player_stats (
|
|
344
|
+
wallet_address VARCHAR(100) PRIMARY KEY,
|
|
345
|
+
total_wagered NUMERIC(20, 9) DEFAULT 0,
|
|
346
|
+
total_won NUMERIC(20, 9) DEFAULT 0,
|
|
347
|
+
net_pnl NUMERIC(20, 9) DEFAULT 0,
|
|
348
|
+
rounds_played INTEGER DEFAULT 0,
|
|
349
|
+
rounds_won INTEGER DEFAULT 0,
|
|
350
|
+
biggest_win NUMERIC(20, 9) DEFAULT 0,
|
|
351
|
+
biggest_win_round INTEGER,
|
|
352
|
+
last_played TIMESTAMP,
|
|
353
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
-- Player history (individual transactions)
|
|
357
|
+
CREATE TABLE IF NOT EXISTS player_history (
|
|
358
|
+
id SERIAL PRIMARY KEY,
|
|
359
|
+
wallet_address VARCHAR(100) NOT NULL,
|
|
360
|
+
round_id INTEGER NOT NULL,
|
|
361
|
+
action VARCHAR(20) NOT NULL,
|
|
362
|
+
amount NUMERIC(20, 9) NOT NULL,
|
|
363
|
+
cumulative_pnl NUMERIC(20, 9) NOT NULL,
|
|
364
|
+
timestamp TIMESTAMP DEFAULT NOW()
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
-- ============================================
|
|
368
|
+
-- 6. JACKPOT SYSTEM
|
|
369
|
+
-- ============================================
|
|
370
|
+
|
|
371
|
+
-- Jackpot rounds history
|
|
372
|
+
CREATE TABLE IF NOT EXISTS jackpot_rounds (
|
|
373
|
+
id SERIAL PRIMARY KEY,
|
|
374
|
+
round_id VARCHAR(50) UNIQUE NOT NULL,
|
|
375
|
+
winner VARCHAR(100) NOT NULL,
|
|
376
|
+
win_amount VARCHAR(50) NOT NULL,
|
|
377
|
+
total_pot VARCHAR(50) NOT NULL,
|
|
378
|
+
entry_count INTEGER NOT NULL,
|
|
379
|
+
locked_at TIMESTAMP,
|
|
380
|
+
resolved_at TIMESTAMP,
|
|
381
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
-- Keeper actions (keeper bot logs)
|
|
385
|
+
CREATE TABLE IF NOT EXISTS keeper_actions (
|
|
386
|
+
id SERIAL PRIMARY KEY,
|
|
387
|
+
round_id INTEGER,
|
|
388
|
+
action_type VARCHAR(50) NOT NULL,
|
|
389
|
+
status VARCHAR(20) NOT NULL,
|
|
390
|
+
details TEXT,
|
|
391
|
+
error_message TEXT,
|
|
392
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
-- Keeper health (monitoring)
|
|
396
|
+
CREATE TABLE IF NOT EXISTS keeper_health (
|
|
397
|
+
id SERIAL PRIMARY KEY,
|
|
398
|
+
round_id INTEGER,
|
|
399
|
+
check_type VARCHAR(50) NOT NULL,
|
|
400
|
+
status VARCHAR(20) NOT NULL,
|
|
401
|
+
message TEXT,
|
|
402
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
-- Keeper rounds (keeper state machine)
|
|
406
|
+
CREATE TABLE IF NOT EXISTS keeper_rounds (
|
|
407
|
+
id SERIAL PRIMARY KEY,
|
|
408
|
+
round_id INTEGER UNIQUE NOT NULL,
|
|
409
|
+
state VARCHAR(50) NOT NULL,
|
|
410
|
+
entries_count INTEGER DEFAULT 0,
|
|
411
|
+
total_pot NUMERIC(20, 9) DEFAULT 0,
|
|
412
|
+
locked_at TIMESTAMP,
|
|
413
|
+
resolved_at TIMESTAMP,
|
|
414
|
+
winner VARCHAR(100),
|
|
415
|
+
last_action TIMESTAMP DEFAULT NOW(),
|
|
416
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
-- ============================================
|
|
420
|
+
-- 7. REFERRAL EARNINGS SYSTEM
|
|
421
|
+
-- ============================================
|
|
422
|
+
-- Commission goes to the GAME CREATOR's referrer ONLY!
|
|
423
|
+
-- The person who referred the GAME CREATOR earns 1% of the ENTIRE POT.
|
|
424
|
+
-- Fee Structure: 4% Operator + 1% Referrer + 1% Oracle = 6% Total
|
|
425
|
+
-- If game creator has no referrer, operator keeps full 5%.
|
|
426
|
+
|
|
427
|
+
-- Main earnings table - tracks each individual earning event
|
|
428
|
+
CREATE TABLE IF NOT EXISTS referral_earnings (
|
|
429
|
+
id SERIAL PRIMARY KEY,
|
|
430
|
+
|
|
431
|
+
-- Relationship: Referrer (who earns) and Referee (who played)
|
|
432
|
+
referrer_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
433
|
+
referrer_wallet VARCHAR(44) NOT NULL,
|
|
434
|
+
referee_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
435
|
+
referee_wallet VARCHAR(44) NOT NULL,
|
|
436
|
+
|
|
437
|
+
-- Game details
|
|
438
|
+
game_id VARCHAR(255) NOT NULL,
|
|
439
|
+
game_type VARCHAR(50) NOT NULL, -- 'sports', 'billiards', 'jackpot'
|
|
440
|
+
|
|
441
|
+
-- Financial details (all in lamports)
|
|
442
|
+
pot_size BIGINT NOT NULL, -- Total pot of the game
|
|
443
|
+
referee_buy_in BIGINT NOT NULL, -- Amount referee wagered
|
|
444
|
+
referee_won BOOLEAN NOT NULL, -- Did referee win?
|
|
445
|
+
referee_payout BIGINT DEFAULT 0, -- Amount referee won (0 if lost)
|
|
446
|
+
|
|
447
|
+
-- Commission details
|
|
448
|
+
commission_rate DECIMAL(6,4) NOT NULL DEFAULT 0.0100, -- 1% = 0.0100
|
|
449
|
+
commission_amount BIGINT NOT NULL, -- Actual commission in lamports
|
|
450
|
+
|
|
451
|
+
-- Status and payout tracking
|
|
452
|
+
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'cancelled')),
|
|
453
|
+
notes TEXT,
|
|
454
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
455
|
+
paid_at TIMESTAMP,
|
|
456
|
+
payout_tx_signature VARCHAR(128),
|
|
457
|
+
payout_batch_id INTEGER,
|
|
458
|
+
notes TEXT, -- Optional notes (e.g., "Paid on-chain during game resolution")
|
|
459
|
+
|
|
460
|
+
-- Prevent duplicate entries for same referee in same game
|
|
461
|
+
UNIQUE(referee_wallet, game_id)
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
-- Payout batches table - tracks batch payouts to referrers
|
|
465
|
+
CREATE TABLE IF NOT EXISTS referral_payout_batches (
|
|
466
|
+
id SERIAL PRIMARY KEY,
|
|
467
|
+
|
|
468
|
+
-- Batch details
|
|
469
|
+
total_amount BIGINT NOT NULL, -- Total lamports in this batch
|
|
470
|
+
num_referrers INTEGER NOT NULL, -- Number of unique referrers paid
|
|
471
|
+
num_earnings INTEGER NOT NULL, -- Number of individual earning records
|
|
472
|
+
|
|
473
|
+
-- Transaction details
|
|
474
|
+
tx_signature VARCHAR(128),
|
|
475
|
+
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed')),
|
|
476
|
+
|
|
477
|
+
-- Timing
|
|
478
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
479
|
+
processed_at TIMESTAMP,
|
|
480
|
+
completed_at TIMESTAMP,
|
|
481
|
+
|
|
482
|
+
-- Error tracking
|
|
483
|
+
error_message TEXT,
|
|
484
|
+
retry_count INTEGER DEFAULT 0
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
-- Referral earnings indexes
|
|
488
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_referrer ON referral_earnings(referrer_user_id, status);
|
|
489
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_referrer_wallet ON referral_earnings(referrer_wallet);
|
|
490
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_referee ON referral_earnings(referee_user_id);
|
|
491
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_game ON referral_earnings(game_id);
|
|
492
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_status ON referral_earnings(status);
|
|
493
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_created ON referral_earnings(created_at DESC);
|
|
494
|
+
CREATE INDEX IF NOT EXISTS idx_referral_earnings_pending ON referral_earnings(referrer_wallet) WHERE status = 'pending';
|
|
495
|
+
CREATE INDEX IF NOT EXISTS idx_payout_batches_status ON referral_payout_batches(status);
|
|
496
|
+
|
|
497
|
+
-- Aggregated summary view for quick dashboard stats
|
|
498
|
+
CREATE OR REPLACE VIEW referral_earnings_summary AS
|
|
499
|
+
SELECT
|
|
500
|
+
u.id as user_id,
|
|
501
|
+
u.wallet_address,
|
|
502
|
+
u.username,
|
|
503
|
+
u.my_referral_code,
|
|
504
|
+
COALESCE(COUNT(re.id), 0) as total_referral_games,
|
|
505
|
+
COALESCE(SUM(re.commission_amount), 0) as total_earned_lamports,
|
|
506
|
+
COALESCE(SUM(CASE WHEN re.status = 'pending' THEN re.commission_amount ELSE 0 END), 0) as pending_lamports,
|
|
507
|
+
COALESCE(SUM(CASE WHEN re.status = 'paid' THEN re.commission_amount ELSE 0 END), 0) as paid_lamports,
|
|
508
|
+
MAX(re.created_at) as last_earning_at
|
|
509
|
+
FROM users u
|
|
510
|
+
LEFT JOIN referral_earnings re ON u.wallet_address = re.referrer_wallet
|
|
511
|
+
WHERE u.my_referral_code IS NOT NULL
|
|
512
|
+
GROUP BY u.id, u.wallet_address, u.username, u.my_referral_code;
|
|
513
|
+
|
|
514
|
+
-- ============================================
|
|
515
|
+
-- 8. WHAT'S NEW SYSTEM (Feature Announcements)
|
|
516
|
+
-- ============================================
|
|
517
|
+
|
|
518
|
+
-- What's New posts (admin announcements with GIFs)
|
|
519
|
+
CREATE TABLE IF NOT EXISTS whats_new_posts (
|
|
520
|
+
id SERIAL PRIMARY KEY,
|
|
521
|
+
|
|
522
|
+
-- Post content
|
|
523
|
+
title VARCHAR(200) NOT NULL,
|
|
524
|
+
content TEXT NOT NULL, -- Markdown-style content
|
|
525
|
+
gif_url TEXT, -- Optional GIF URL to showcase feature
|
|
526
|
+
|
|
527
|
+
-- Categorization
|
|
528
|
+
category VARCHAR(50) DEFAULT 'feature', -- feature, improvement, bugfix, announcement
|
|
529
|
+
|
|
530
|
+
-- Versioning
|
|
531
|
+
version VARCHAR(20), -- Optional app version (e.g., "1.2.0")
|
|
532
|
+
|
|
533
|
+
-- Targeting (future use - could target specific user segments)
|
|
534
|
+
target_all BOOLEAN DEFAULT TRUE,
|
|
535
|
+
|
|
536
|
+
-- Status
|
|
537
|
+
is_published BOOLEAN DEFAULT TRUE,
|
|
538
|
+
is_pinned BOOLEAN DEFAULT FALSE, -- Pinned posts show at top
|
|
539
|
+
|
|
540
|
+
-- Audit
|
|
541
|
+
created_by VARCHAR(64) NOT NULL, -- Admin wallet address
|
|
542
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
543
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
-- User What's New reads (tracking which users have seen which posts)
|
|
547
|
+
CREATE TABLE IF NOT EXISTS user_whats_new_reads (
|
|
548
|
+
id SERIAL PRIMARY KEY,
|
|
549
|
+
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
550
|
+
post_id INTEGER NOT NULL REFERENCES whats_new_posts(id) ON DELETE CASCADE,
|
|
551
|
+
read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
552
|
+
|
|
553
|
+
-- Prevent duplicates
|
|
554
|
+
UNIQUE(user_id, post_id)
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
-- What's New indexes
|
|
558
|
+
CREATE INDEX IF NOT EXISTS idx_whats_new_posts_published
|
|
559
|
+
ON whats_new_posts(is_published, created_at DESC);
|
|
560
|
+
CREATE INDEX IF NOT EXISTS idx_whats_new_posts_pinned
|
|
561
|
+
ON whats_new_posts(is_pinned, created_at DESC);
|
|
562
|
+
CREATE INDEX IF NOT EXISTS idx_user_whats_new_reads_user
|
|
563
|
+
ON user_whats_new_reads(user_id);
|
|
564
|
+
CREATE INDEX IF NOT EXISTS idx_user_whats_new_reads_post
|
|
565
|
+
ON user_whats_new_reads(post_id);
|
|
566
|
+
|
|
567
|
+
-- Function to update updated_at timestamp for whats_new_posts
|
|
568
|
+
CREATE OR REPLACE FUNCTION update_whats_new_updated_at()
|
|
569
|
+
RETURNS TRIGGER AS $$
|
|
570
|
+
BEGIN
|
|
571
|
+
NEW.updated_at = NOW();
|
|
572
|
+
RETURN NEW;
|
|
573
|
+
END;
|
|
574
|
+
$$ LANGUAGE plpgsql;
|
|
575
|
+
|
|
576
|
+
-- Trigger to auto-update updated_at
|
|
577
|
+
DROP TRIGGER IF EXISTS whats_new_posts_updated_at ON whats_new_posts;
|
|
578
|
+
CREATE TRIGGER whats_new_posts_updated_at
|
|
579
|
+
BEFORE UPDATE ON whats_new_posts
|
|
580
|
+
FOR EACH ROW
|
|
581
|
+
EXECUTE FUNCTION update_whats_new_updated_at();
|
|
582
|
+
|
|
583
|
+
-- ============================================
|
|
584
|
+
-- 9. DEVELOPER API PLATFORM
|
|
585
|
+
-- ============================================
|
|
586
|
+
|
|
587
|
+
-- Developer accounts (linked to Solana wallet)
|
|
588
|
+
CREATE TABLE IF NOT EXISTS developer_accounts (
|
|
589
|
+
id SERIAL PRIMARY KEY,
|
|
590
|
+
wallet_address VARCHAR(44) UNIQUE NOT NULL,
|
|
591
|
+
display_name VARCHAR(100),
|
|
592
|
+
email VARCHAR(255),
|
|
593
|
+
commission_wallet VARCHAR(44) NOT NULL,
|
|
594
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
595
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
-- Developer apps (each gets API keys)
|
|
599
|
+
CREATE TABLE IF NOT EXISTS developer_apps (
|
|
600
|
+
id SERIAL PRIMARY KEY,
|
|
601
|
+
developer_id INTEGER REFERENCES developer_accounts(id),
|
|
602
|
+
app_name VARCHAR(100) NOT NULL,
|
|
603
|
+
description TEXT,
|
|
604
|
+
website_url VARCHAR(500),
|
|
605
|
+
environment VARCHAR(10) DEFAULT 'sandbox',
|
|
606
|
+
status VARCHAR(20) DEFAULT 'active',
|
|
607
|
+
network_mode VARCHAR(10) DEFAULT 'open' CHECK (network_mode IN ('open', 'private')),
|
|
608
|
+
ui_config JSONB DEFAULT '{}',
|
|
609
|
+
resolution_secret VARCHAR(64),
|
|
610
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
611
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
-- API keys (sandbox + production per app)
|
|
615
|
+
CREATE TABLE IF NOT EXISTS developer_api_keys (
|
|
616
|
+
id SERIAL PRIMARY KEY,
|
|
617
|
+
app_id INTEGER REFERENCES developer_apps(id),
|
|
618
|
+
key_prefix VARCHAR(20) NOT NULL,
|
|
619
|
+
key_hash VARCHAR(64) NOT NULL,
|
|
620
|
+
key_hint VARCHAR(8) NOT NULL,
|
|
621
|
+
environment VARCHAR(10) NOT NULL,
|
|
622
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
623
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
624
|
+
last_used_at TIMESTAMPTZ
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
-- Track which games came from which developer app
|
|
628
|
+
CREATE TABLE IF NOT EXISTS developer_game_attributions (
|
|
629
|
+
id SERIAL PRIMARY KEY,
|
|
630
|
+
game_id VARCHAR(100) NOT NULL,
|
|
631
|
+
app_id INTEGER REFERENCES developer_apps(id),
|
|
632
|
+
developer_id INTEGER REFERENCES developer_accounts(id),
|
|
633
|
+
commission_wallet VARCHAR(44) NOT NULL,
|
|
634
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
-- API usage logs for rate limiting and analytics
|
|
638
|
+
CREATE TABLE IF NOT EXISTS developer_api_logs (
|
|
639
|
+
id SERIAL PRIMARY KEY,
|
|
640
|
+
app_id INTEGER REFERENCES developer_apps(id),
|
|
641
|
+
endpoint VARCHAR(200) NOT NULL,
|
|
642
|
+
method VARCHAR(10) NOT NULL,
|
|
643
|
+
status_code INTEGER,
|
|
644
|
+
response_time_ms INTEGER,
|
|
645
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
-- Developer webhooks (outbound notifications)
|
|
649
|
+
CREATE TABLE IF NOT EXISTS developer_webhooks (
|
|
650
|
+
id SERIAL PRIMARY KEY,
|
|
651
|
+
app_id INTEGER NOT NULL REFERENCES developer_apps(id) ON DELETE CASCADE,
|
|
652
|
+
url VARCHAR(500) NOT NULL,
|
|
653
|
+
secret VARCHAR(64) NOT NULL,
|
|
654
|
+
events TEXT[] NOT NULL DEFAULT '{}',
|
|
655
|
+
is_active BOOLEAN DEFAULT TRUE,
|
|
656
|
+
description VARCHAR(200),
|
|
657
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
658
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
-- Developer webhook delivery logs
|
|
662
|
+
CREATE TABLE IF NOT EXISTS developer_webhook_logs (
|
|
663
|
+
id SERIAL PRIMARY KEY,
|
|
664
|
+
webhook_id INTEGER NOT NULL REFERENCES developer_webhooks(id) ON DELETE CASCADE,
|
|
665
|
+
event VARCHAR(50) NOT NULL,
|
|
666
|
+
payload JSONB NOT NULL,
|
|
667
|
+
status_code INTEGER,
|
|
668
|
+
response_body TEXT,
|
|
669
|
+
attempts INTEGER DEFAULT 1,
|
|
670
|
+
success BOOLEAN DEFAULT FALSE,
|
|
671
|
+
error TEXT,
|
|
672
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
-- Developer app users (tracks which users authenticated through which app)
|
|
676
|
+
CREATE TABLE IF NOT EXISTS developer_app_users (
|
|
677
|
+
developer_app_id INTEGER NOT NULL REFERENCES developer_apps(id) ON DELETE CASCADE,
|
|
678
|
+
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
679
|
+
first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
680
|
+
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
681
|
+
PRIMARY KEY (developer_app_id, user_id)
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
-- Developer platform indexes
|
|
685
|
+
CREATE INDEX IF NOT EXISTS idx_developer_accounts_wallet ON developer_accounts(wallet_address);
|
|
686
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON developer_api_keys(key_hash);
|
|
687
|
+
CREATE INDEX IF NOT EXISTS idx_game_attributions_game ON developer_game_attributions(game_id);
|
|
688
|
+
CREATE INDEX IF NOT EXISTS idx_game_attributions_app ON developer_game_attributions(app_id);
|
|
689
|
+
CREATE INDEX IF NOT EXISTS idx_api_logs_app ON developer_api_logs(app_id, created_at);
|
|
690
|
+
CREATE INDEX IF NOT EXISTS idx_webhooks_app ON developer_webhooks(app_id);
|
|
691
|
+
CREATE INDEX IF NOT EXISTS idx_webhook_logs_webhook ON developer_webhook_logs(webhook_id, created_at DESC);
|
|
692
|
+
CREATE INDEX IF NOT EXISTS idx_developer_app_users_app ON developer_app_users(developer_app_id);
|
|
693
|
+
CREATE INDEX IF NOT EXISTS idx_developer_app_users_user ON developer_app_users(user_id);
|
|
694
|
+
CREATE INDEX IF NOT EXISTS idx_developer_app_users_last_seen ON developer_app_users(developer_app_id, last_seen_at DESC);
|
|
695
|
+
|
|
696
|
+
-- ============================================
|
|
697
|
+
-- 10. MIGRATIONS (Add missing columns to existing tables)
|
|
698
|
+
-- ============================================
|
|
699
|
+
|
|
700
|
+
-- Add buy_in column to user_game_refs if it doesn't exist
|
|
701
|
+
DO $$
|
|
702
|
+
BEGIN
|
|
703
|
+
IF NOT EXISTS (
|
|
704
|
+
SELECT 1 FROM information_schema.columns
|
|
705
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'buy_in'
|
|
706
|
+
) THEN
|
|
707
|
+
ALTER TABLE user_game_refs ADD COLUMN buy_in NUMERIC(20, 9);
|
|
708
|
+
END IF;
|
|
709
|
+
|
|
710
|
+
IF NOT EXISTS (
|
|
711
|
+
SELECT 1 FROM information_schema.columns
|
|
712
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'won_game'
|
|
713
|
+
) THEN
|
|
714
|
+
ALTER TABLE user_game_refs ADD COLUMN won_game BOOLEAN DEFAULT false;
|
|
715
|
+
END IF;
|
|
716
|
+
END $$;
|
|
717
|
+
|
|
718
|
+
-- Add check constraint for preferred_currency if it doesn't exist
|
|
719
|
+
DO $$
|
|
720
|
+
BEGIN
|
|
721
|
+
IF NOT EXISTS (
|
|
722
|
+
SELECT 1 FROM information_schema.table_constraints
|
|
723
|
+
WHERE constraint_name = 'check_preferred_currency'
|
|
724
|
+
) THEN
|
|
725
|
+
ALTER TABLE users
|
|
726
|
+
ADD CONSTRAINT check_preferred_currency
|
|
727
|
+
CHECK (preferred_currency IN ('USD', 'EUR', 'CAD', 'GBP', 'JPY', 'AUD', 'CHF', 'CNY', 'SEK', 'NZD'));
|
|
728
|
+
END IF;
|
|
729
|
+
END $$;
|
|
730
|
+
|
|
731
|
+
-- Add comment for preferred_currency column
|
|
732
|
+
COMMENT ON COLUMN users.preferred_currency IS 'User preferred display currency (3-letter code)';
|
|
733
|
+
|
|
734
|
+
-- Add notification tracking columns to games table if they don't exist
|
|
735
|
+
DO $$
|
|
736
|
+
BEGIN
|
|
737
|
+
IF NOT EXISTS (
|
|
738
|
+
SELECT 1 FROM information_schema.columns
|
|
739
|
+
WHERE table_name = 'games' AND column_name = 'lock_notification_sent_10min'
|
|
740
|
+
) THEN
|
|
741
|
+
ALTER TABLE games ADD COLUMN lock_notification_sent_10min BOOLEAN DEFAULT false;
|
|
742
|
+
END IF;
|
|
743
|
+
|
|
744
|
+
IF NOT EXISTS (
|
|
745
|
+
SELECT 1 FROM information_schema.columns
|
|
746
|
+
WHERE table_name = 'games' AND column_name = 'lock_notification_sent_now'
|
|
747
|
+
) THEN
|
|
748
|
+
ALTER TABLE games ADD COLUMN lock_notification_sent_now BOOLEAN DEFAULT false;
|
|
749
|
+
END IF;
|
|
750
|
+
|
|
751
|
+
-- Add matchup_image_url column for pre-generated S3 matchup images
|
|
752
|
+
IF NOT EXISTS (
|
|
753
|
+
SELECT 1 FROM information_schema.columns
|
|
754
|
+
WHERE table_name = 'games' AND column_name = 'matchup_image_url'
|
|
755
|
+
) THEN
|
|
756
|
+
ALTER TABLE games ADD COLUMN matchup_image_url TEXT;
|
|
757
|
+
END IF;
|
|
758
|
+
|
|
759
|
+
-- Add draw_team_players column for EPL/soccer draw betting
|
|
760
|
+
IF NOT EXISTS (
|
|
761
|
+
SELECT 1 FROM information_schema.columns
|
|
762
|
+
WHERE table_name = 'games' AND column_name = 'draw_team_players'
|
|
763
|
+
) THEN
|
|
764
|
+
ALTER TABLE games ADD COLUMN draw_team_players TEXT[] DEFAULT '{}';
|
|
765
|
+
END IF;
|
|
766
|
+
|
|
767
|
+
-- Add Connect4 specific columns
|
|
768
|
+
IF NOT EXISTS (
|
|
769
|
+
SELECT 1 FROM information_schema.columns
|
|
770
|
+
WHERE table_name = 'games' AND column_name = 'game_status'
|
|
771
|
+
) THEN
|
|
772
|
+
ALTER TABLE games ADD COLUMN game_status VARCHAR(50);
|
|
773
|
+
END IF;
|
|
774
|
+
|
|
775
|
+
IF NOT EXISTS (
|
|
776
|
+
SELECT 1 FROM information_schema.columns
|
|
777
|
+
WHERE table_name = 'games' AND column_name = 'connect4_board'
|
|
778
|
+
) THEN
|
|
779
|
+
ALTER TABLE games ADD COLUMN connect4_board JSONB;
|
|
780
|
+
END IF;
|
|
781
|
+
|
|
782
|
+
IF NOT EXISTS (
|
|
783
|
+
SELECT 1 FROM information_schema.columns
|
|
784
|
+
WHERE table_name = 'games' AND column_name = 'connect4_current_turn'
|
|
785
|
+
) THEN
|
|
786
|
+
ALTER TABLE games ADD COLUMN connect4_current_turn VARCHAR(10);
|
|
787
|
+
END IF;
|
|
788
|
+
|
|
789
|
+
IF NOT EXISTS (
|
|
790
|
+
SELECT 1 FROM information_schema.columns
|
|
791
|
+
WHERE table_name = 'games' AND column_name = 'connect4_winner'
|
|
792
|
+
) THEN
|
|
793
|
+
ALTER TABLE games ADD COLUMN connect4_winner VARCHAR(10);
|
|
794
|
+
END IF;
|
|
795
|
+
|
|
796
|
+
IF NOT EXISTS (
|
|
797
|
+
SELECT 1 FROM information_schema.columns
|
|
798
|
+
WHERE table_name = 'games' AND column_name = 'connect4_winning_cells'
|
|
799
|
+
) THEN
|
|
800
|
+
ALTER TABLE games ADD COLUMN connect4_winning_cells JSONB;
|
|
801
|
+
END IF;
|
|
802
|
+
|
|
803
|
+
IF NOT EXISTS (
|
|
804
|
+
SELECT 1 FROM information_schema.columns
|
|
805
|
+
WHERE table_name = 'games' AND column_name = 'claim_signature'
|
|
806
|
+
) THEN
|
|
807
|
+
ALTER TABLE games ADD COLUMN claim_signature TEXT;
|
|
808
|
+
END IF;
|
|
809
|
+
|
|
810
|
+
IF NOT EXISTS (
|
|
811
|
+
SELECT 1 FROM information_schema.columns
|
|
812
|
+
WHERE table_name = 'games' AND column_name = 'completed_at'
|
|
813
|
+
) THEN
|
|
814
|
+
ALTER TABLE games ADD COLUMN completed_at TIMESTAMP;
|
|
815
|
+
END IF;
|
|
816
|
+
|
|
817
|
+
IF NOT EXISTS (
|
|
818
|
+
SELECT 1 FROM information_schema.columns
|
|
819
|
+
WHERE table_name = 'games' AND column_name = 'invited_player'
|
|
820
|
+
) THEN
|
|
821
|
+
ALTER TABLE games ADD COLUMN invited_player VARCHAR(255);
|
|
822
|
+
END IF;
|
|
823
|
+
END $$;
|
|
824
|
+
|
|
825
|
+
-- Add indexes for Connect4 columns
|
|
826
|
+
CREATE INDEX IF NOT EXISTS idx_games_game_status ON games(game_status);
|
|
827
|
+
CREATE INDEX IF NOT EXISTS idx_games_type_status ON games(game_type, game_status);
|
|
828
|
+
CREATE INDEX IF NOT EXISTS idx_games_invited_player ON games(invited_player) WHERE invited_player IS NOT NULL;
|
|
829
|
+
|
|
830
|
+
-- ============================================
|
|
831
|
+
-- 11. FOREIGN KEY CONSTRAINTS
|
|
832
|
+
-- ============================================
|
|
833
|
+
|
|
834
|
+
-- Add foreign keys if they don't exist
|
|
835
|
+
DO $$
|
|
836
|
+
BEGIN
|
|
837
|
+
-- Chat messages reply_to_id
|
|
838
|
+
IF NOT EXISTS (
|
|
839
|
+
SELECT 1 FROM information_schema.table_constraints
|
|
840
|
+
WHERE constraint_name = 'chat_messages_reply_to_id_fkey'
|
|
841
|
+
) THEN
|
|
842
|
+
ALTER TABLE chat_messages
|
|
843
|
+
ADD CONSTRAINT chat_messages_reply_to_id_fkey
|
|
844
|
+
FOREIGN KEY (reply_to_id) REFERENCES chat_messages(id) ON DELETE SET NULL;
|
|
845
|
+
END IF;
|
|
846
|
+
|
|
847
|
+
-- Chat reactions message_id
|
|
848
|
+
IF NOT EXISTS (
|
|
849
|
+
SELECT 1 FROM information_schema.table_constraints
|
|
850
|
+
WHERE constraint_name = 'chat_reactions_message_id_fkey'
|
|
851
|
+
) THEN
|
|
852
|
+
ALTER TABLE chat_reactions
|
|
853
|
+
ADD CONSTRAINT chat_reactions_message_id_fkey
|
|
854
|
+
FOREIGN KEY (message_id) REFERENCES chat_messages(id) ON DELETE CASCADE;
|
|
855
|
+
END IF;
|
|
856
|
+
|
|
857
|
+
-- Chat notifications message_id
|
|
858
|
+
IF NOT EXISTS (
|
|
859
|
+
SELECT 1 FROM information_schema.table_constraints
|
|
860
|
+
WHERE constraint_name = 'chat_notifications_message_id_fkey'
|
|
861
|
+
) THEN
|
|
862
|
+
ALTER TABLE chat_notifications
|
|
863
|
+
ADD CONSTRAINT chat_notifications_message_id_fkey
|
|
864
|
+
FOREIGN KEY (message_id) REFERENCES chat_messages(id) ON DELETE CASCADE;
|
|
865
|
+
END IF;
|
|
866
|
+
END $$;
|
|
867
|
+
|
|
868
|
+
-- ============================================
|
|
869
|
+
-- 12. INDEXES FOR PERFORMANCE
|
|
870
|
+
-- ============================================
|
|
871
|
+
|
|
872
|
+
-- Users indexes
|
|
873
|
+
CREATE INDEX IF NOT EXISTS idx_users_wallet ON users(wallet_address);
|
|
874
|
+
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
|
875
|
+
CREATE INDEX IF NOT EXISTS idx_users_referral_code ON users(referral_code);
|
|
876
|
+
CREATE INDEX IF NOT EXISTS idx_users_telegram_user_id ON users(telegram_user_id) WHERE telegram_user_id IS NOT NULL;
|
|
877
|
+
CREATE INDEX IF NOT EXISTS idx_users_preferred_currency ON users(preferred_currency);
|
|
878
|
+
|
|
879
|
+
-- Push subscriptions indexes
|
|
880
|
+
CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id);
|
|
881
|
+
|
|
882
|
+
-- Auth indexes
|
|
883
|
+
CREATE INDEX IF NOT EXISTS idx_nonces_expires ON auth_nonces(expires_at);
|
|
884
|
+
CREATE INDEX IF NOT EXISTS idx_user_sessions_wallet ON user_sessions(wallet_address);
|
|
885
|
+
CREATE INDEX IF NOT EXISTS idx_user_sessions_token_hash ON user_sessions(token_hash);
|
|
886
|
+
CREATE INDEX IF NOT EXISTS idx_user_sessions_expires ON user_sessions(expires_at);
|
|
887
|
+
|
|
888
|
+
-- User badges indexes
|
|
889
|
+
CREATE INDEX IF NOT EXISTS idx_user_badges_user_id ON user_badges(user_id);
|
|
890
|
+
CREATE INDEX IF NOT EXISTS idx_user_badges_type ON user_badges(badge_type);
|
|
891
|
+
CREATE INDEX IF NOT EXISTS idx_user_badges_earned ON user_badges(earned_at DESC);
|
|
892
|
+
|
|
893
|
+
-- Chat indexes
|
|
894
|
+
CREATE INDEX IF NOT EXISTS idx_chat_timestamp ON chat_messages(timestamp DESC);
|
|
895
|
+
CREATE INDEX IF NOT EXISTS idx_chat_wallet ON chat_messages(wallet_address);
|
|
896
|
+
CREATE INDEX IF NOT EXISTS idx_chat_user_id ON chat_messages(user_id);
|
|
897
|
+
CREATE INDEX IF NOT EXISTS idx_chat_reply_to ON chat_messages(reply_to_id);
|
|
898
|
+
CREATE INDEX IF NOT EXISTS idx_chat_winner ON chat_messages(is_winner_announcement);
|
|
899
|
+
CREATE INDEX IF NOT EXISTS idx_chat_game_invite ON chat_messages((game_invite_metadata->>'gameId')) WHERE game_invite_metadata IS NOT NULL;
|
|
900
|
+
CREATE INDEX IF NOT EXISTS idx_reactions_message ON chat_reactions(message_id);
|
|
901
|
+
CREATE INDEX IF NOT EXISTS idx_relationships_user ON user_relationships(user_id);
|
|
902
|
+
CREATE INDEX IF NOT EXISTS idx_relationships_type ON user_relationships(relationship_type);
|
|
903
|
+
CREATE INDEX IF NOT EXISTS idx_notifications_user ON chat_notifications(user_id);
|
|
904
|
+
CREATE INDEX IF NOT EXISTS idx_message_mentions_user ON message_mentions(mentioned_user_id);
|
|
905
|
+
CREATE INDEX IF NOT EXISTS idx_message_mentions_message ON message_mentions(message_id);
|
|
906
|
+
CREATE INDEX IF NOT EXISTS idx_payments_message ON chat_payments(message_id);
|
|
907
|
+
CREATE INDEX IF NOT EXISTS idx_payments_sender ON chat_payments(sender_user_id);
|
|
908
|
+
CREATE INDEX IF NOT EXISTS idx_payments_recipient ON chat_payments(recipient_user_id);
|
|
909
|
+
CREATE INDEX IF NOT EXISTS idx_payments_signature ON chat_payments(transaction_signature);
|
|
910
|
+
|
|
911
|
+
-- Games indexes
|
|
912
|
+
CREATE INDEX IF NOT EXISTS idx_games_game_id ON games(game_id);
|
|
913
|
+
CREATE INDEX IF NOT EXISTS idx_games_creator ON games(created_by);
|
|
914
|
+
CREATE INDEX IF NOT EXISTS idx_games_status ON games(automatic_status);
|
|
915
|
+
CREATE INDEX IF NOT EXISTS idx_user_game_refs_wallet ON user_game_refs(wallet_address);
|
|
916
|
+
CREATE INDEX IF NOT EXISTS idx_user_game_refs_game ON user_game_refs(game_id);
|
|
917
|
+
CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id);
|
|
918
|
+
CREATE INDEX IF NOT EXISTS idx_audit_logs_type ON audit_logs(log_type);
|
|
919
|
+
|
|
920
|
+
-- Social indexes
|
|
921
|
+
CREATE INDEX IF NOT EXISTS idx_friends_user ON friends(user_wallet);
|
|
922
|
+
CREATE INDEX IF NOT EXISTS idx_friends_status ON friends(status);
|
|
923
|
+
CREATE INDEX IF NOT EXISTS idx_friend_requests_from ON friend_requests(from_user_id);
|
|
924
|
+
CREATE INDEX IF NOT EXISTS idx_friend_requests_to ON friend_requests(to_user_id);
|
|
925
|
+
CREATE INDEX IF NOT EXISTS idx_friend_requests_status ON friend_requests(status);
|
|
926
|
+
CREATE INDEX IF NOT EXISTS idx_group_members_group ON group_members(group_id);
|
|
927
|
+
CREATE INDEX IF NOT EXISTS idx_group_members_user ON group_members(user_id);
|
|
928
|
+
|
|
929
|
+
-- Player stats indexes
|
|
930
|
+
CREATE INDEX IF NOT EXISTS idx_player_stats_pnl ON player_stats(net_pnl DESC);
|
|
931
|
+
CREATE INDEX IF NOT EXISTS idx_player_stats_wagered ON player_stats(total_wagered DESC);
|
|
932
|
+
CREATE INDEX IF NOT EXISTS idx_player_history_wallet ON player_history(wallet_address, timestamp DESC);
|
|
933
|
+
CREATE INDEX IF NOT EXISTS idx_player_history_round ON player_history(round_id);
|
|
934
|
+
|
|
935
|
+
-- Keeper indexes
|
|
936
|
+
CREATE INDEX IF NOT EXISTS idx_keeper_actions_round ON keeper_actions(round_id);
|
|
937
|
+
CREATE INDEX IF NOT EXISTS idx_keeper_health_round ON keeper_health(round_id);
|
|
938
|
+
CREATE INDEX IF NOT EXISTS idx_keeper_rounds_state ON keeper_rounds(state);
|
|
939
|
+
|
|
940
|
+
-- ============================================
|
|
941
|
+
-- 13. VIEWS FOR MONITORING
|
|
942
|
+
-- ============================================
|
|
943
|
+
|
|
944
|
+
-- Stuck rounds view (for keeper monitoring)
|
|
945
|
+
CREATE OR REPLACE VIEW stuck_rounds AS
|
|
946
|
+
SELECT
|
|
947
|
+
round_id,
|
|
948
|
+
state,
|
|
949
|
+
entries_count,
|
|
950
|
+
last_action,
|
|
951
|
+
(NOW() - last_action) as time_stuck
|
|
952
|
+
FROM keeper_rounds
|
|
953
|
+
WHERE state IN ('open', 'locked')
|
|
954
|
+
AND (NOW() - last_action) > INTERVAL '10 minutes'
|
|
955
|
+
ORDER BY last_action ASC;
|
|
956
|
+
|
|
957
|
+
-- Keeper health summary
|
|
958
|
+
CREATE OR REPLACE VIEW keeper_health_summary AS
|
|
959
|
+
SELECT
|
|
960
|
+
DATE(created_at) as date,
|
|
961
|
+
check_type,
|
|
962
|
+
status,
|
|
963
|
+
COUNT(*) as count
|
|
964
|
+
FROM keeper_health
|
|
965
|
+
WHERE created_at > NOW() - INTERVAL '7 days'
|
|
966
|
+
GROUP BY DATE(created_at), check_type, status
|
|
967
|
+
ORDER BY date DESC, check_type;
|
|
968
|
+
|
|
969
|
+
-- ============================================
|
|
970
|
+
-- 14. SUMMARY
|
|
971
|
+
-- ============================================
|
|
972
|
+
|
|
973
|
+
-- Show all tables
|
|
974
|
+
\echo '✅ Database schema setup complete!'
|
|
975
|
+
\echo ''
|
|
976
|
+
\echo 'Tables created:'
|
|
977
|
+
SELECT table_name
|
|
978
|
+
FROM information_schema.tables
|
|
979
|
+
WHERE table_schema = 'public'
|
|
980
|
+
AND table_type = 'BASE TABLE'
|
|
981
|
+
ORDER BY table_name;
|
|
982
|
+
|
|
983
|
+
\echo ''
|
|
984
|
+
\echo 'Views created:'
|
|
985
|
+
SELECT table_name
|
|
986
|
+
FROM information_schema.views
|
|
987
|
+
WHERE table_schema = 'public'
|
|
988
|
+
ORDER BY table_name;
|
|
989
|
+
|
|
990
|
+
\echo ''
|
|
991
|
+
\echo '🎉 All done! Your database is ready for the DUBS server.'
|
|
992
|
+
|