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,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📢 What's New API Routes
|
|
3
|
+
*
|
|
4
|
+
* Public endpoints for viewing posts
|
|
5
|
+
* Admin-protected endpoints for creating/editing/deleting
|
|
6
|
+
*
|
|
7
|
+
* Admin wallet: Hvv1ctqHLR5wonuuRguefS6EpGUe7tFRBX2YWHGr3mes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const express = require('express');
|
|
11
|
+
const router = express.Router();
|
|
12
|
+
const multer = require('multer');
|
|
13
|
+
const { authenticate, optionalAuth } = require('../middleware/authenticate');
|
|
14
|
+
const whatsNewService = require('../services/whatsNewService');
|
|
15
|
+
const S3Service = require('../services/s3Service');
|
|
16
|
+
|
|
17
|
+
// Initialize S3 service for GIF uploads
|
|
18
|
+
const s3Service = new S3Service();
|
|
19
|
+
|
|
20
|
+
// Configure multer for memory storage (we'll upload to S3)
|
|
21
|
+
const upload = multer({
|
|
22
|
+
storage: multer.memoryStorage(),
|
|
23
|
+
limits: {
|
|
24
|
+
fileSize: 10 * 1024 * 1024, // 10MB max
|
|
25
|
+
},
|
|
26
|
+
fileFilter: (req, file, cb) => {
|
|
27
|
+
const allowedTypes = ['image/gif', 'image/png', 'image/jpeg', 'image/webp'];
|
|
28
|
+
if (allowedTypes.includes(file.mimetype)) {
|
|
29
|
+
cb(null, true);
|
|
30
|
+
} else {
|
|
31
|
+
cb(new Error('Invalid file type. Only GIF, PNG, JPEG, and WebP are allowed.'));
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Middleware: Require admin wallet
|
|
38
|
+
*/
|
|
39
|
+
function requireAdmin(req, res, next) {
|
|
40
|
+
if (!req.user || !whatsNewService.isAdmin(req.user.walletAddress)) {
|
|
41
|
+
return res.status(403).json({
|
|
42
|
+
success: false,
|
|
43
|
+
error: 'Admin access required',
|
|
44
|
+
code: 'ADMIN_REQUIRED',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ====================================
|
|
51
|
+
// PUBLIC ENDPOINTS (optionally authenticated)
|
|
52
|
+
// ====================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* GET /whats-new/posts
|
|
56
|
+
* Get all published posts (with read status if authenticated)
|
|
57
|
+
*/
|
|
58
|
+
router.get('/posts', optionalAuth, async (req, res) => {
|
|
59
|
+
try {
|
|
60
|
+
const userId = req.user?.userId || null;
|
|
61
|
+
const posts = await whatsNewService.getPosts(userId);
|
|
62
|
+
|
|
63
|
+
res.json({
|
|
64
|
+
success: true,
|
|
65
|
+
posts,
|
|
66
|
+
count: posts.length,
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('[WhatsNew] GET /posts error:', error);
|
|
70
|
+
res.status(500).json({
|
|
71
|
+
success: false,
|
|
72
|
+
error: error.message,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* GET /whats-new/unread-count
|
|
79
|
+
* Get count of unread posts for authenticated user
|
|
80
|
+
*/
|
|
81
|
+
router.get('/unread-count', authenticate, async (req, res) => {
|
|
82
|
+
try {
|
|
83
|
+
const { userId } = req.user;
|
|
84
|
+
const count = await whatsNewService.getUnreadCount(userId);
|
|
85
|
+
|
|
86
|
+
res.json({
|
|
87
|
+
success: true,
|
|
88
|
+
count,
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('[WhatsNew] GET /unread-count error:', error);
|
|
92
|
+
res.status(500).json({
|
|
93
|
+
success: false,
|
|
94
|
+
error: error.message,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ====================================
|
|
100
|
+
// USER ENDPOINTS (authenticated)
|
|
101
|
+
// ====================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* POST /whats-new/mark-read
|
|
105
|
+
* Mark specific posts as read
|
|
106
|
+
*/
|
|
107
|
+
router.post('/mark-read', authenticate, async (req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const { userId } = req.user;
|
|
110
|
+
const { postIds } = req.body;
|
|
111
|
+
|
|
112
|
+
if (!Array.isArray(postIds)) {
|
|
113
|
+
return res.status(400).json({
|
|
114
|
+
success: false,
|
|
115
|
+
error: 'postIds must be an array',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await whatsNewService.markAsRead(userId, postIds);
|
|
120
|
+
|
|
121
|
+
res.json({
|
|
122
|
+
success: true,
|
|
123
|
+
});
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('[WhatsNew] POST /mark-read error:', error);
|
|
126
|
+
res.status(500).json({
|
|
127
|
+
success: false,
|
|
128
|
+
error: error.message,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* POST /whats-new/mark-all-read
|
|
135
|
+
* Mark all posts as read for current user
|
|
136
|
+
*/
|
|
137
|
+
router.post('/mark-all-read', authenticate, async (req, res) => {
|
|
138
|
+
try {
|
|
139
|
+
const { userId } = req.user;
|
|
140
|
+
const count = await whatsNewService.markAllAsRead(userId);
|
|
141
|
+
|
|
142
|
+
res.json({
|
|
143
|
+
success: true,
|
|
144
|
+
markedCount: count,
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('[WhatsNew] POST /mark-all-read error:', error);
|
|
148
|
+
res.status(500).json({
|
|
149
|
+
success: false,
|
|
150
|
+
error: error.message,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ====================================
|
|
156
|
+
// ADMIN ENDPOINTS
|
|
157
|
+
// ====================================
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* GET /whats-new/admin/posts
|
|
161
|
+
* Get all posts including unpublished (admin only)
|
|
162
|
+
*/
|
|
163
|
+
router.get('/admin/posts', authenticate, requireAdmin, async (req, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const posts = await whatsNewService.getAllPostsAdmin();
|
|
166
|
+
|
|
167
|
+
res.json({
|
|
168
|
+
success: true,
|
|
169
|
+
posts,
|
|
170
|
+
count: posts.length,
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error('[WhatsNew] GET /admin/posts error:', error);
|
|
174
|
+
res.status(500).json({
|
|
175
|
+
success: false,
|
|
176
|
+
error: error.message,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* GET /whats-new/admin/check
|
|
183
|
+
* Check if current user is admin
|
|
184
|
+
*/
|
|
185
|
+
router.get('/admin/check', authenticate, (req, res) => {
|
|
186
|
+
const isAdmin = whatsNewService.isAdmin(req.user.walletAddress);
|
|
187
|
+
|
|
188
|
+
res.json({
|
|
189
|
+
success: true,
|
|
190
|
+
isAdmin,
|
|
191
|
+
walletAddress: req.user.walletAddress,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* POST /whats-new/admin/posts
|
|
197
|
+
* Create a new post (admin only)
|
|
198
|
+
*/
|
|
199
|
+
router.post('/admin/posts', authenticate, requireAdmin, async (req, res) => {
|
|
200
|
+
try {
|
|
201
|
+
const { title, content, gifUrl, category, version, isPinned, isPublished } = req.body;
|
|
202
|
+
|
|
203
|
+
if (!title || !content) {
|
|
204
|
+
return res.status(400).json({
|
|
205
|
+
success: false,
|
|
206
|
+
error: 'Title and content are required',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const post = await whatsNewService.createPost({
|
|
211
|
+
title,
|
|
212
|
+
content,
|
|
213
|
+
gifUrl,
|
|
214
|
+
category,
|
|
215
|
+
version,
|
|
216
|
+
isPinned,
|
|
217
|
+
isPublished,
|
|
218
|
+
createdBy: req.user.walletAddress,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
res.json({
|
|
222
|
+
success: true,
|
|
223
|
+
post,
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('[WhatsNew] POST /admin/posts error:', error);
|
|
227
|
+
res.status(500).json({
|
|
228
|
+
success: false,
|
|
229
|
+
error: error.message,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* PUT /whats-new/admin/posts/:id
|
|
236
|
+
* Update a post (admin only)
|
|
237
|
+
*/
|
|
238
|
+
router.put('/admin/posts/:id', authenticate, requireAdmin, async (req, res) => {
|
|
239
|
+
try {
|
|
240
|
+
const postId = parseInt(req.params.id, 10);
|
|
241
|
+
const { title, content, gifUrl, category, version, isPinned, isPublished } = req.body;
|
|
242
|
+
|
|
243
|
+
const post = await whatsNewService.updatePost(postId, {
|
|
244
|
+
title,
|
|
245
|
+
content,
|
|
246
|
+
gifUrl,
|
|
247
|
+
category,
|
|
248
|
+
version,
|
|
249
|
+
isPinned,
|
|
250
|
+
isPublished,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
res.json({
|
|
254
|
+
success: true,
|
|
255
|
+
post,
|
|
256
|
+
});
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error('[WhatsNew] PUT /admin/posts/:id error:', error);
|
|
259
|
+
res.status(500).json({
|
|
260
|
+
success: false,
|
|
261
|
+
error: error.message,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* DELETE /whats-new/admin/posts/:id
|
|
268
|
+
* Delete a post (admin only)
|
|
269
|
+
*/
|
|
270
|
+
router.delete('/admin/posts/:id', authenticate, requireAdmin, async (req, res) => {
|
|
271
|
+
try {
|
|
272
|
+
const postId = parseInt(req.params.id, 10);
|
|
273
|
+
await whatsNewService.deletePost(postId);
|
|
274
|
+
|
|
275
|
+
res.json({
|
|
276
|
+
success: true,
|
|
277
|
+
});
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error('[WhatsNew] DELETE /admin/posts/:id error:', error);
|
|
280
|
+
res.status(500).json({
|
|
281
|
+
success: false,
|
|
282
|
+
error: error.message,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* POST /whats-new/admin/upload-gif
|
|
289
|
+
* Upload a GIF to S3 (admin only)
|
|
290
|
+
* Returns the public URL to use in posts
|
|
291
|
+
*/
|
|
292
|
+
router.post('/admin/upload-gif', authenticate, requireAdmin, upload.single('gif'), async (req, res) => {
|
|
293
|
+
try {
|
|
294
|
+
if (!req.file) {
|
|
295
|
+
return res.status(400).json({
|
|
296
|
+
success: false,
|
|
297
|
+
error: 'No file uploaded',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!s3Service.isConfigured()) {
|
|
302
|
+
return res.status(500).json({
|
|
303
|
+
success: false,
|
|
304
|
+
error: 'S3 is not configured. Please set AWS credentials.',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
console.log(`[WhatsNew] Uploading GIF: ${req.file.originalname} (${req.file.size} bytes)`);
|
|
309
|
+
|
|
310
|
+
const result = await s3Service.uploadWhatsNewGif(
|
|
311
|
+
req.file.buffer,
|
|
312
|
+
req.file.originalname,
|
|
313
|
+
req.file.mimetype
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
res.json({
|
|
317
|
+
success: true,
|
|
318
|
+
gifUrl: result.publicUrl,
|
|
319
|
+
key: result.key,
|
|
320
|
+
});
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('[WhatsNew] POST /admin/upload-gif error:', error);
|
|
323
|
+
res.status(500).json({
|
|
324
|
+
success: false,
|
|
325
|
+
error: error.message,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
module.exports = router;
|
|
331
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(chmod:*)",
|
|
5
|
+
"Bash(SOLANA_RPC_URL=\"https://solana-mainnet.g.alchemy.com/v2/M7pyy3QL4xYOndpcYukNhf-yq2IG6eyl\" node:*)",
|
|
6
|
+
"Bash(heroku pg:psql:*)",
|
|
7
|
+
"Bash(curl:*)",
|
|
8
|
+
"Bash(node scripts/authority-resolve-game.js:*)",
|
|
9
|
+
"Bash(solana balance:*)",
|
|
10
|
+
"Bash(heroku addons:info:*)",
|
|
11
|
+
"Bash(heroku addons:plans:*)",
|
|
12
|
+
"Bash(heroku addons:upgrade:*)"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# 🔧 DUBS Server - Database Scripts
|
|
2
|
+
|
|
3
|
+
Quick reference for database setup and maintenance scripts.
|
|
4
|
+
|
|
5
|
+
## 🚀 Main Setup Scripts
|
|
6
|
+
|
|
7
|
+
### `setup-database-fresh.sh` ⭐ **RECOMMENDED**
|
|
8
|
+
|
|
9
|
+
**Complete automated database setup - everything in one command!**
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
./scripts/setup-database-fresh.sh
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**What it does:**
|
|
16
|
+
- ✅ Checks if PostgreSQL is installed and running
|
|
17
|
+
- ✅ Starts PostgreSQL automatically if needed
|
|
18
|
+
- ✅ Creates the `dubs_db` database (with confirmation if exists)
|
|
19
|
+
- ✅ Runs complete schema setup (all 18 tables)
|
|
20
|
+
- ✅ Creates `.env` file if missing
|
|
21
|
+
- ✅ Verifies everything works
|
|
22
|
+
|
|
23
|
+
**Safe to run multiple times!** It won't break existing data.
|
|
24
|
+
|
|
25
|
+
### `setup-complete-database.sql`
|
|
26
|
+
|
|
27
|
+
**Complete SQL schema - all tables, indexes, and views**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
psql -d dubs_db -f scripts/setup-complete-database.sql
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Creates all 18 tables:
|
|
34
|
+
- 4 Authentication & User tables
|
|
35
|
+
- 4 Chat system tables
|
|
36
|
+
- 4 Social feature tables
|
|
37
|
+
- 2 Player stats tables
|
|
38
|
+
- 4 Jackpot system tables
|
|
39
|
+
|
|
40
|
+
## 📋 Legacy Scripts
|
|
41
|
+
|
|
42
|
+
These scripts are kept for specific use cases but `setup-database-fresh.sh` handles everything.
|
|
43
|
+
|
|
44
|
+
### `setup-auth-tables.js`
|
|
45
|
+
Creates authentication-related tables (users, auth_nonces, etc.)
|
|
46
|
+
```bash
|
|
47
|
+
node scripts/setup-auth-tables.js
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### `setup-keeper-database.js`
|
|
51
|
+
Creates jackpot keeper tables (keeper_rounds, keeper_actions, etc.)
|
|
52
|
+
```bash
|
|
53
|
+
node scripts/setup-keeper-database.js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `setup-pnl-tracking.js`
|
|
57
|
+
Creates player stats tracking tables
|
|
58
|
+
```bash
|
|
59
|
+
node scripts/setup-pnl-tracking.js
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 🗑️ Data Management Scripts
|
|
63
|
+
|
|
64
|
+
### `purge-all-data.sh` ⚠️ **DESTRUCTIVE**
|
|
65
|
+
|
|
66
|
+
**Removes ALL data from ALL tables (keeps structure)**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
./scripts/purge-all-data.sh
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Features:**
|
|
73
|
+
- ⚠️ Double confirmation required (type 'yes' + database name)
|
|
74
|
+
- 🗑️ Deletes all data from all tables
|
|
75
|
+
- ✅ Preserves table structure and schema
|
|
76
|
+
- 🔄 Resets ID sequences to 1
|
|
77
|
+
- 📊 Shows before/after counts
|
|
78
|
+
- 🔒 Cannot be undone!
|
|
79
|
+
|
|
80
|
+
**When to use:**
|
|
81
|
+
- Reset development environment
|
|
82
|
+
- Clear test data before demos
|
|
83
|
+
- Start fresh after testing
|
|
84
|
+
- Remove sensitive data
|
|
85
|
+
|
|
86
|
+
**Alternative SQL version:**
|
|
87
|
+
```bash
|
|
88
|
+
psql -d dubs_db -f scripts/purge-all-data.sql
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 🔍 Utility Scripts
|
|
92
|
+
|
|
93
|
+
### `status.js`
|
|
94
|
+
Check keeper and jackpot status
|
|
95
|
+
```bash
|
|
96
|
+
node scripts/status.js
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `check-chat-schema.js`
|
|
100
|
+
Verify chat database schema
|
|
101
|
+
```bash
|
|
102
|
+
node scripts/check-chat-schema.js
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `cleanup-database.js`
|
|
106
|
+
Clean up old/expired data
|
|
107
|
+
```bash
|
|
108
|
+
node scripts/cleanup-database.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 🎯 Quick Commands
|
|
112
|
+
|
|
113
|
+
### Fresh Start
|
|
114
|
+
```bash
|
|
115
|
+
./scripts/setup-database-fresh.sh
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Reset Existing Database
|
|
119
|
+
```bash
|
|
120
|
+
dropdb dubs_db
|
|
121
|
+
createdb dubs_db
|
|
122
|
+
psql -d dubs_db -f scripts/setup-complete-database.sql
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Update Schema Only (Keep Data)
|
|
126
|
+
```bash
|
|
127
|
+
psql -d dubs_db -f scripts/setup-complete-database.sql
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Check What's Installed
|
|
131
|
+
```bash
|
|
132
|
+
psql -d dubs_db -c "\dt" # List tables
|
|
133
|
+
psql -d dubs_db -c "\dv" # List views
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 📖 Full Documentation
|
|
137
|
+
|
|
138
|
+
See [DATABASE_SETUP_GUIDE.md](../documentation/DATABASE_SETUP_GUIDE.md) for:
|
|
139
|
+
- Complete troubleshooting guide
|
|
140
|
+
- Database management commands
|
|
141
|
+
- Backup/restore procedures
|
|
142
|
+
- Common error solutions
|
|
143
|
+
- Security best practices
|
|
144
|
+
|
|
145
|
+
## 🆘 Quick Troubleshooting
|
|
146
|
+
|
|
147
|
+
**PostgreSQL not running?**
|
|
148
|
+
```bash
|
|
149
|
+
brew services start postgresql@14
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Database doesn't exist?**
|
|
153
|
+
```bash
|
|
154
|
+
createdb dubs_db
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Tables missing?**
|
|
158
|
+
```bash
|
|
159
|
+
psql -d dubs_db -f scripts/setup-complete-database.sql
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Start over?**
|
|
163
|
+
```bash
|
|
164
|
+
./scripts/setup-database-fresh.sh
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
💡 **Pro tip:** Bookmark this file and the setup script - they'll save you time on every new environment!
|
|
170
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 🔄 Complete Restart Script
|
|
4
|
+
# Use this to restart everything with the latest code
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "🔄 Restarting Dubs Jackpot System"
|
|
9
|
+
echo "=================================="
|
|
10
|
+
echo ""
|
|
11
|
+
|
|
12
|
+
# Kill all existing processes
|
|
13
|
+
echo "1️⃣ Stopping all processes..."
|
|
14
|
+
pkill -f "jackpot/keeper.js" || true
|
|
15
|
+
pkill -f "server.js.*dubs-server" || true
|
|
16
|
+
sleep 2
|
|
17
|
+
|
|
18
|
+
# Create logs directory if needed
|
|
19
|
+
mkdir -p logs
|
|
20
|
+
|
|
21
|
+
# Start with PM2 if available
|
|
22
|
+
if command -v pm2 &> /dev/null; then
|
|
23
|
+
echo "2️⃣ Starting with PM2..."
|
|
24
|
+
|
|
25
|
+
# Stop existing PM2 processes
|
|
26
|
+
pm2 delete dubs-api 2>/dev/null || true
|
|
27
|
+
pm2 delete jackpot-keeper 2>/dev/null || true
|
|
28
|
+
|
|
29
|
+
# Start using ecosystem config
|
|
30
|
+
pm2 start ecosystem.config.js
|
|
31
|
+
|
|
32
|
+
# Save PM2 configuration
|
|
33
|
+
pm2 save
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "✅ Started with PM2!"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "📊 View status:"
|
|
39
|
+
echo " pm2 status"
|
|
40
|
+
echo ""
|
|
41
|
+
echo "📋 View logs:"
|
|
42
|
+
echo " pm2 logs dubs-api"
|
|
43
|
+
echo " pm2 logs jackpot-keeper"
|
|
44
|
+
echo ""
|
|
45
|
+
echo "🔄 Restart individual service:"
|
|
46
|
+
echo " pm2 restart dubs-api"
|
|
47
|
+
echo " pm2 restart jackpot-keeper"
|
|
48
|
+
|
|
49
|
+
else
|
|
50
|
+
echo "⚠️ PM2 not installed. Starting manually..."
|
|
51
|
+
echo ""
|
|
52
|
+
echo "Install PM2 for better process management:"
|
|
53
|
+
echo " npm install -g pm2"
|
|
54
|
+
echo ""
|
|
55
|
+
|
|
56
|
+
# Start manually in background
|
|
57
|
+
echo "Starting API server..."
|
|
58
|
+
SOLANA_NETWORK=https://api.devnet.solana.com nohup node server.js > logs/api.log 2>&1 &
|
|
59
|
+
echo "API Server PID: $!"
|
|
60
|
+
|
|
61
|
+
sleep 2
|
|
62
|
+
|
|
63
|
+
echo "Starting keeper bot..."
|
|
64
|
+
nohup node scripts/jackpot/keeper.js > logs/keeper.log 2>&1 &
|
|
65
|
+
echo "Keeper Bot PID: $!"
|
|
66
|
+
|
|
67
|
+
echo ""
|
|
68
|
+
echo "✅ Started manually!"
|
|
69
|
+
echo ""
|
|
70
|
+
echo "📋 View logs:"
|
|
71
|
+
echo " tail -f logs/api.log"
|
|
72
|
+
echo " tail -f logs/keeper.log"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Wait for services to start
|
|
76
|
+
sleep 3
|
|
77
|
+
|
|
78
|
+
# Health check
|
|
79
|
+
echo ""
|
|
80
|
+
echo "3️⃣ Health check..."
|
|
81
|
+
|
|
82
|
+
if curl -s http://localhost:3001/jackpot/health > /dev/null 2>&1; then
|
|
83
|
+
echo "✅ API server is responding"
|
|
84
|
+
else
|
|
85
|
+
echo "❌ API server is NOT responding!"
|
|
86
|
+
echo " Check logs for errors"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Check current round
|
|
91
|
+
ROUND=$(curl -s http://localhost:3001/jackpot/round/current 2>/dev/null)
|
|
92
|
+
if echo "$ROUND" | grep -q "roundId"; then
|
|
93
|
+
ROUND_ID=$(echo "$ROUND" | grep -o '"roundId":"[^"]*"' | cut -d'"' -f4)
|
|
94
|
+
ROUND_STATUS=$(echo "$ROUND" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
|
|
95
|
+
echo "✅ Round $ROUND_ID is $ROUND_STATUS"
|
|
96
|
+
else
|
|
97
|
+
echo "⚠️ No active round"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
echo ""
|
|
101
|
+
echo "=================================="
|
|
102
|
+
echo "🎉 System restarted successfully!"
|
|
103
|
+
echo "=================================="
|
|
104
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- Add claim tracking columns to user_game_refs table
|
|
2
|
+
|
|
3
|
+
DO $$
|
|
4
|
+
BEGIN
|
|
5
|
+
-- Add claimed_at column
|
|
6
|
+
IF NOT EXISTS (
|
|
7
|
+
SELECT 1 FROM information_schema.columns
|
|
8
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'claimed_at'
|
|
9
|
+
) THEN
|
|
10
|
+
ALTER TABLE user_game_refs ADD COLUMN claimed_at TIMESTAMP;
|
|
11
|
+
RAISE NOTICE 'Added claimed_at column';
|
|
12
|
+
END IF;
|
|
13
|
+
|
|
14
|
+
-- Add claim_signature column
|
|
15
|
+
IF NOT EXISTS (
|
|
16
|
+
SELECT 1 FROM information_schema.columns
|
|
17
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'claim_signature'
|
|
18
|
+
) THEN
|
|
19
|
+
ALTER TABLE user_game_refs ADD COLUMN claim_signature TEXT;
|
|
20
|
+
RAISE NOTICE 'Added claim_signature column';
|
|
21
|
+
END IF;
|
|
22
|
+
|
|
23
|
+
-- Add claim_explorer_url column
|
|
24
|
+
IF NOT EXISTS (
|
|
25
|
+
SELECT 1 FROM information_schema.columns
|
|
26
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'claim_explorer_url'
|
|
27
|
+
) THEN
|
|
28
|
+
ALTER TABLE user_game_refs ADD COLUMN claim_explorer_url TEXT;
|
|
29
|
+
RAISE NOTICE 'Added claim_explorer_url column';
|
|
30
|
+
END IF;
|
|
31
|
+
|
|
32
|
+
-- Add amount_claimed column
|
|
33
|
+
IF NOT EXISTS (
|
|
34
|
+
SELECT 1 FROM information_schema.columns
|
|
35
|
+
WHERE table_name = 'user_game_refs' AND column_name = 'amount_claimed'
|
|
36
|
+
) THEN
|
|
37
|
+
ALTER TABLE user_game_refs ADD COLUMN amount_claimed DECIMAL(20, 9);
|
|
38
|
+
RAISE NOTICE 'Added amount_claimed column';
|
|
39
|
+
END IF;
|
|
40
|
+
END $$;
|
|
41
|
+
|
|
42
|
+
-- Verify columns were added
|
|
43
|
+
SELECT column_name, data_type
|
|
44
|
+
FROM information_schema.columns
|
|
45
|
+
WHERE table_name = 'user_game_refs'
|
|
46
|
+
AND column_name IN ('claimed_at', 'claim_signature', 'claim_explorer_url', 'amount_claimed')
|
|
47
|
+
ORDER BY column_name;
|
|
48
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Migration: Add crypto_prices_cache table
|
|
2
|
+
-- Purpose: Store cached crypto prices from CoinGecko API to reduce API calls
|
|
3
|
+
-- Date: 2025-12-06
|
|
4
|
+
|
|
5
|
+
-- Create crypto prices cache table
|
|
6
|
+
CREATE TABLE IF NOT EXISTS crypto_prices_cache (
|
|
7
|
+
id SERIAL PRIMARY KEY,
|
|
8
|
+
crypto_id VARCHAR(50) NOT NULL UNIQUE,
|
|
9
|
+
prices JSONB NOT NULL,
|
|
10
|
+
last_updated TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
11
|
+
expires_at TIMESTAMP NOT NULL,
|
|
12
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- Create indexes for performance
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_crypto_prices_id ON crypto_prices_cache(crypto_id);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_crypto_prices_expires ON crypto_prices_cache(expires_at);
|
|
18
|
+
|
|
19
|
+
-- Add comments for documentation
|
|
20
|
+
COMMENT ON TABLE crypto_prices_cache IS 'Caches crypto prices from CoinGecko API (5-minute TTL)';
|
|
21
|
+
COMMENT ON COLUMN crypto_prices_cache.crypto_id IS 'CoinGecko crypto ID (e.g., solana, bitcoin)';
|
|
22
|
+
COMMENT ON COLUMN crypto_prices_cache.prices IS 'JSON object containing prices in multiple fiat currencies';
|
|
23
|
+
COMMENT ON COLUMN crypto_prices_cache.last_updated IS 'Timestamp when prices were last fetched from API';
|
|
24
|
+
COMMENT ON COLUMN crypto_prices_cache.expires_at IS 'Timestamp when cache entry expires';
|
|
25
|
+
|
|
26
|
+
COMMIT;
|
|
27
|
+
|