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.
Files changed (304) hide show
  1. package/.claude/settings.local.json +280 -0
  2. package/CLAUDE.md +46 -0
  3. package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
  4. package/CURRENT_SESSION.md +171 -0
  5. package/CURRENT_SESSION_DRAW.md +516 -0
  6. package/MARCH_MADNESS_SURVIVOR.md +254 -0
  7. package/PANDA.md +166 -0
  8. package/Procfile +4 -0
  9. package/README.md +476 -0
  10. package/controllers/livescoresController.js +376 -0
  11. package/controllers/pickemController.js +554 -0
  12. package/controllers/survivorAdminController.js +887 -0
  13. package/controllers/survivorController.js +623 -0
  14. package/cron/oracleMonitor.js +77 -0
  15. package/cron/pickemOracleMonitor.js +73 -0
  16. package/data/jackpot-history.json +952 -0
  17. package/data/ncaaTeams.js +406 -0
  18. package/documentation/API_SECURITY_GUIDE.md +327 -0
  19. package/documentation/ARCADE_API.md +593 -0
  20. package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
  21. package/documentation/ARCADE_QUICKSTART.md +242 -0
  22. package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
  23. package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
  24. package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
  25. package/documentation/CLAIM_STATUS_FIX.md +67 -0
  26. package/documentation/CLI_TOOL_GUIDE.md +372 -0
  27. package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
  28. package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
  29. package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
  30. package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
  31. package/documentation/DATABASE_QUERIES.md +269 -0
  32. package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
  33. package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
  34. package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
  35. package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
  36. package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
  37. package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
  38. package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
  39. package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
  40. package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
  41. package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
  42. package/documentation/HEROKU_DEPLOYMENT.md +134 -0
  43. package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
  44. package/documentation/JACKPOT_API.md +521 -0
  45. package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
  46. package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
  47. package/documentation/JWT_QUICK_SETUP.md +268 -0
  48. package/documentation/JWT_TESTING_GUIDE.md +404 -0
  49. package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
  50. package/documentation/KEEPER_SETUP.md +206 -0
  51. package/documentation/KEEPER_STATE_MACHINE.md +423 -0
  52. package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
  53. package/documentation/LOCAL_VOTING_TEST.md +279 -0
  54. package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
  55. package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
  56. package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
  57. package/documentation/PNL_TRACKING_SETUP.md +189 -0
  58. package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
  59. package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
  60. package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
  61. package/documentation/QUICK_AUTH_SETUP.md +99 -0
  62. package/documentation/QUICK_DEPLOY.md +224 -0
  63. package/documentation/QUICK_FIX.md +114 -0
  64. package/documentation/QUICK_START.md +152 -0
  65. package/documentation/REFEREE_MODE_GUIDE.md +392 -0
  66. package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
  67. package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
  68. package/documentation/RUN_MIGRATION_NOW.md +39 -0
  69. package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
  70. package/documentation/SETUP_GUIDE.md +184 -0
  71. package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
  72. package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
  73. package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
  74. package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
  75. package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
  76. package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
  77. package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
  78. package/ecosystem.config.js +65 -0
  79. package/env.template +125 -0
  80. package/middleware/apiKeyAuth.js +136 -0
  81. package/middleware/authenticate.js +214 -0
  82. package/middleware/developerUserAuth.js +76 -0
  83. package/middleware/socketAuth.js +69 -0
  84. package/package.json +49 -0
  85. package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
  86. package/postman/Dubs-API-v1.postman_collection.json +205 -0
  87. package/postman/Dubs_Developer_API.postman_collection.json +662 -0
  88. package/postman/QUICKSTART.md +118 -0
  89. package/postman/QUICK_REFERENCE.md +246 -0
  90. package/postman/README.md +71 -0
  91. package/postman/VOTING_API_GUIDE.md +426 -0
  92. package/refactor/Animations.md +148 -0
  93. package/refactor/Chat.md +252 -0
  94. package/routes/actionsRoutes.js +699 -0
  95. package/routes/adminRoutes.js +370 -0
  96. package/routes/analyticsRoutes.js +1262 -0
  97. package/routes/arcadeRoutes.js +557 -0
  98. package/routes/authRoutes.js +2310 -0
  99. package/routes/avatarRoutes.js +85 -0
  100. package/routes/botRoutes.js +211 -0
  101. package/routes/chatRoutes.js +377 -0
  102. package/routes/cryptoPriceRoutes.js +105 -0
  103. package/routes/developerRoutes.js +4201 -0
  104. package/routes/deviceRoutes.js +214 -0
  105. package/routes/dmRoutes.js +167 -0
  106. package/routes/esportsRoutes.js +806 -0
  107. package/routes/exchangeRateRoutes.js +233 -0
  108. package/routes/gamesRoutes.js +3028 -0
  109. package/routes/jackpotRoutes.js +754 -0
  110. package/routes/keeperMonitoringRoutes.js +156 -0
  111. package/routes/keeperWebhookRoutes.js +466 -0
  112. package/routes/livescoresRoutes.js +31 -0
  113. package/routes/pickemAdminRoutes.js +199 -0
  114. package/routes/pickemRoutes.js +231 -0
  115. package/routes/playerStatsRoutes.js +147 -0
  116. package/routes/portfolioRoutes.js +217 -0
  117. package/routes/promoRoutes.js +418 -0
  118. package/routes/referralEarningsRoutes.js +392 -0
  119. package/routes/socialRoutes.js +459 -0
  120. package/routes/sportsRoutes.js +1271 -0
  121. package/routes/survivorAdminRoutes.js +345 -0
  122. package/routes/survivorRoutes.js +756 -0
  123. package/routes/uploadRoutes.js +256 -0
  124. package/routes/userProfileRoutes.js +244 -0
  125. package/routes/whatsNewRoutes.js +331 -0
  126. package/scripts/.claude/settings.local.json +15 -0
  127. package/scripts/README.md +170 -0
  128. package/scripts/RESTART_EVERYTHING.sh +104 -0
  129. package/scripts/add-claim-columns.sql +48 -0
  130. package/scripts/add-crypto-prices-cache.sql +27 -0
  131. package/scripts/add-exchange-rates-cache.sql +40 -0
  132. package/scripts/add-game-invite-column.sql +23 -0
  133. package/scripts/add-game-invite-notification.sql +33 -0
  134. package/scripts/add-game-invite-telegram-pref.sql +16 -0
  135. package/scripts/add-game-joined-notification.sql +16 -0
  136. package/scripts/add-game-joined-pref.js +40 -0
  137. package/scripts/add-game-joined-preference.sql +6 -0
  138. package/scripts/add-game-start-notifications.sql +41 -0
  139. package/scripts/add-notification-flags-to-games.sql +55 -0
  140. package/scripts/add-pending-game-dismissals.sql +19 -0
  141. package/scripts/add-preferred-currency.sql +34 -0
  142. package/scripts/add-winner-columns.js +61 -0
  143. package/scripts/add_mention_system.sql +53 -0
  144. package/scripts/add_payment_system.sql +96 -0
  145. package/scripts/add_sports_event_id_column.sql +22 -0
  146. package/scripts/analyze-cohort-data-heroku.js +276 -0
  147. package/scripts/analyze-cohort-data.js +295 -0
  148. package/scripts/analyze-prod-cohorts.sh +10 -0
  149. package/scripts/backfill-matchup-images.js +245 -0
  150. package/scripts/backfill-missing-signatures.js +175 -0
  151. package/scripts/backfill-referral-earnings.js +202 -0
  152. package/scripts/check-chat-schema.js +130 -0
  153. package/scripts/check-db.sh +14 -0
  154. package/scripts/check_oracle_in_game.js +54 -0
  155. package/scripts/cleanup-database.js +193 -0
  156. package/scripts/clear-notification-cache.js +85 -0
  157. package/scripts/convert-mnemonic.js +50 -0
  158. package/scripts/create-users-table.sql +44 -0
  159. package/scripts/debug-cohort-counts.js +248 -0
  160. package/scripts/debug-winner-calc.js +84 -0
  161. package/scripts/deploy-payment-system.sh +118 -0
  162. package/scripts/deploy-to-heroku.sh +63 -0
  163. package/scripts/diagnose-locked-round.js +143 -0
  164. package/scripts/dubs-cli.js +720 -0
  165. package/scripts/dump-account.js +65 -0
  166. package/scripts/find-vrf-offset.js +48 -0
  167. package/scripts/fix-chat-notifications-constraint.sql +122 -0
  168. package/scripts/fix-claim-columns.js +124 -0
  169. package/scripts/fix-constraint-now.js +44 -0
  170. package/scripts/fix-lock-timestamps.js +96 -0
  171. package/scripts/fix-locked-round.sh +126 -0
  172. package/scripts/fix-missing-badges.sql +91 -0
  173. package/scripts/fix-payment-notifications.sql +41 -0
  174. package/scripts/force-new-round.js +55 -0
  175. package/scripts/force-resolve-and-claim.js +278 -0
  176. package/scripts/important/README.md +115 -0
  177. package/scripts/important/authority-force-lock.js +197 -0
  178. package/scripts/important/authority-resolve-game.js +267 -0
  179. package/scripts/important/check-game-status.js +373 -0
  180. package/scripts/important/list-pending-games-by-version.js +270 -0
  181. package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
  182. package/scripts/initialize-jackpot.js +111 -0
  183. package/scripts/jackpot/.claude/settings.local.json +10 -0
  184. package/scripts/jackpot/force-reset.js +84 -0
  185. package/scripts/jackpot/initialize-mainnet.js +100 -0
  186. package/scripts/jackpot/keeper.js +742 -0
  187. package/scripts/jackpot/status.js +107 -0
  188. package/scripts/jackpot/update-round-duration.js +143 -0
  189. package/scripts/keeper-bot.js +112 -0
  190. package/scripts/list-pending-games.js +131 -0
  191. package/scripts/migrate-chat-v2.js +127 -0
  192. package/scripts/migrate-chat-winners.js +84 -0
  193. package/scripts/migrate-chat.sh +17 -0
  194. package/scripts/migrate-game-invite.js +83 -0
  195. package/scripts/migrate-heroku-game-notifications.sh +159 -0
  196. package/scripts/migrations/001_analytics_tables.sql +422 -0
  197. package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
  198. package/scripts/migrations/003_referral_earnings.sql +208 -0
  199. package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
  200. package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
  201. package/scripts/migrations/005_push_notifications.sql +55 -0
  202. package/scripts/migrations/006_add_draw_team_players.sql +28 -0
  203. package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
  204. package/scripts/migrations/007_add_gif_url.sql +8 -0
  205. package/scripts/migrations/008_add_connect4_columns.sql +139 -0
  206. package/scripts/migrations/008_add_pool_tracking.sql +22 -0
  207. package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
  208. package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
  209. package/scripts/migrations/011_create_developer_tables.sql +67 -0
  210. package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
  211. package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
  212. package/scripts/migrations/013_add_network_mode.sql +18 -0
  213. package/scripts/migrations/014_create_developer_app_users.sql +19 -0
  214. package/scripts/migrations/015_add_ui_config.sql +4 -0
  215. package/scripts/migrations/016_add_resolution_secret.sql +4 -0
  216. package/scripts/migrations/017_add_external_game_id.sql +3 -0
  217. package/scripts/migrations/018_create_pickem_tables.sql +115 -0
  218. package/scripts/migrations/019_expo_push_tokens.sql +19 -0
  219. package/scripts/migrations/create_whats_new_tables.sql +88 -0
  220. package/scripts/migrations/drop_live_games_tables.sql +34 -0
  221. package/scripts/open-jackpot-round.js +85 -0
  222. package/scripts/purge-all-data.sh +329 -0
  223. package/scripts/purge-all-data.sql +142 -0
  224. package/scripts/purge-heroku-data.sh +149 -0
  225. package/scripts/purge-heroku-data.sql +62 -0
  226. package/scripts/rebuild-heroku-database.sh +113 -0
  227. package/scripts/recover-funds.js +357 -0
  228. package/scripts/regenerate-epl-images.js +278 -0
  229. package/scripts/resize-s3-matchup-images.js +374 -0
  230. package/scripts/resolve-direct.js +88 -0
  231. package/scripts/resolve-mock-game.js +124 -0
  232. package/scripts/resolve-pickem-game.js +55 -0
  233. package/scripts/resolve-round-manual.js +83 -0
  234. package/scripts/resolve-stuck-game.js +382 -0
  235. package/scripts/resolve-stuck-round.js +42 -0
  236. package/scripts/run-connect4-migration.sh +16 -0
  237. package/scripts/run-mention-migration.sh +32 -0
  238. package/scripts/run-payment-migration.sh +51 -0
  239. package/scripts/run-preferred-currency-migration.sh +31 -0
  240. package/scripts/run-referral-earnings-migration.sh +32 -0
  241. package/scripts/run-survivor-outcome-migration.sh +16 -0
  242. package/scripts/seed-test-users.js +346 -0
  243. package/scripts/setup-auth-tables.js +78 -0
  244. package/scripts/setup-complete-database.sql +992 -0
  245. package/scripts/setup-database-fresh.sh +359 -0
  246. package/scripts/setup-heroku-keeper.sh +48 -0
  247. package/scripts/setup-keeper-database.js +83 -0
  248. package/scripts/setup-keeper-state-db.sql +110 -0
  249. package/scripts/setup-oracle.sh +39 -0
  250. package/scripts/setup-pnl-tracking.js +111 -0
  251. package/scripts/start-devnet.sh +14 -0
  252. package/scripts/test-arcade-devnet.sh +160 -0
  253. package/scripts/test-arcade-match.sh +109 -0
  254. package/scripts/test-automatic-mode.sh +239 -0
  255. package/scripts/test-connect4-cancel-claim.js +370 -0
  256. package/scripts/test-connect4-e2e.js +369 -0
  257. package/scripts/test-connect4-resolve.js +369 -0
  258. package/scripts/test-game-state-endpoint.js +136 -0
  259. package/scripts/test-invite-notification.js +86 -0
  260. package/scripts/test-jackpot-api.sh +71 -0
  261. package/scripts/test-poll-confirmation.js +267 -0
  262. package/scripts/test-resolve-game.js +271 -0
  263. package/scripts/test-resolve-signature.js +223 -0
  264. package/scripts/test-signature-preservation.js +124 -0
  265. package/scripts/test-state-machine.js +291 -0
  266. package/scripts/test-webhook-receiver.js +60 -0
  267. package/scripts/update-notification-constraint.js +52 -0
  268. package/scripts/verify-account-layout.js +145 -0
  269. package/scripts/verify-winner-algorithm.js +278 -0
  270. package/server.js +5259 -0
  271. package/services/arcadeMatchService.js +763 -0
  272. package/services/automaticGameOracle.js +1596 -0
  273. package/services/chatService.js +1612 -0
  274. package/services/connect4GameService.js +1049 -0
  275. package/services/connect4NotificationService.js +374 -0
  276. package/services/cryptoPriceService.js +223 -0
  277. package/services/customGameResolver.js +260 -0
  278. package/services/db.js +79 -0
  279. package/services/directMessageService.js +389 -0
  280. package/services/discordNotifications.js +160 -0
  281. package/services/exchangeRateService.js +289 -0
  282. package/services/expoPushService.js +314 -0
  283. package/services/gamesCacheService.js +539 -0
  284. package/services/jackpotHistory.js +331 -0
  285. package/services/jackpotService.js +856 -0
  286. package/services/keeperStateService.js +355 -0
  287. package/services/matchupImageService.js +591 -0
  288. package/services/notificationCacheService.js +407 -0
  289. package/services/pickemOracle.js +440 -0
  290. package/services/playerStatsService.js +389 -0
  291. package/services/portfolioService.js +555 -0
  292. package/services/promoService.js +757 -0
  293. package/services/promoTreasuryService.js +239 -0
  294. package/services/pushNotifications.js +353 -0
  295. package/services/redisService.js +422 -0
  296. package/services/referralEarningsService.js +728 -0
  297. package/services/s3Service.js +396 -0
  298. package/services/socialService.js +1202 -0
  299. package/services/survivorOracle.js +469 -0
  300. package/services/survivorSimulator.js +475 -0
  301. package/services/telegramNotifications.js +461 -0
  302. package/services/userProfileStatsService.js +1185 -0
  303. package/services/whatsNewService.js +388 -0
  304. 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
+