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,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📱 Device Settings API Routes
|
|
3
|
+
*
|
|
4
|
+
* Public endpoints for device-level settings (no authentication required)
|
|
5
|
+
* Used for cross-browser state like "Add to Home Screen" banner dismissal
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const express = require('express');
|
|
9
|
+
const router = express.Router();
|
|
10
|
+
const { pool } = require('../services/db');
|
|
11
|
+
|
|
12
|
+
// Initialize device_settings table
|
|
13
|
+
async function initializeDeviceTable() {
|
|
14
|
+
if (!process.env.DATABASE_URL) {
|
|
15
|
+
console.log('⚠️ Device settings table skipped: DATABASE_URL not set');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Create table if it doesn't exist
|
|
21
|
+
await pool.query(`
|
|
22
|
+
CREATE TABLE IF NOT EXISTS device_settings (
|
|
23
|
+
id SERIAL PRIMARY KEY,
|
|
24
|
+
device_id VARCHAR(255) UNIQUE NOT NULL,
|
|
25
|
+
wallet_address VARCHAR(44),
|
|
26
|
+
dismissed_a2hs BOOLEAN DEFAULT false,
|
|
27
|
+
platform VARCHAR(50),
|
|
28
|
+
user_agent TEXT,
|
|
29
|
+
last_seen TIMESTAMP DEFAULT NOW(),
|
|
30
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
31
|
+
);
|
|
32
|
+
`);
|
|
33
|
+
|
|
34
|
+
// Add wallet_address column if it doesn't exist (migration for existing tables)
|
|
35
|
+
await pool.query(`
|
|
36
|
+
DO $$
|
|
37
|
+
BEGIN
|
|
38
|
+
IF NOT EXISTS (
|
|
39
|
+
SELECT 1 FROM information_schema.columns
|
|
40
|
+
WHERE table_name = 'device_settings'
|
|
41
|
+
AND column_name = 'wallet_address'
|
|
42
|
+
) THEN
|
|
43
|
+
ALTER TABLE device_settings ADD COLUMN wallet_address VARCHAR(44);
|
|
44
|
+
RAISE NOTICE 'Added wallet_address column to device_settings';
|
|
45
|
+
END IF;
|
|
46
|
+
END $$;
|
|
47
|
+
`);
|
|
48
|
+
|
|
49
|
+
// Create indexes
|
|
50
|
+
await pool.query(`
|
|
51
|
+
CREATE INDEX IF NOT EXISTS idx_device_id ON device_settings(device_id);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_wallet_address ON device_settings(wallet_address);
|
|
53
|
+
`);
|
|
54
|
+
|
|
55
|
+
console.log('✅ Device settings table ready');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('❌ Failed to initialize device_settings table:', error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
initializeDeviceTable();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* GET /api/device/check-dismissal
|
|
65
|
+
* Check if A2HS was dismissed for this wallet
|
|
66
|
+
* Query params: walletAddress (REQUIRED)
|
|
67
|
+
*/
|
|
68
|
+
router.get('/check-dismissal', async (req, res) => {
|
|
69
|
+
try {
|
|
70
|
+
const { walletAddress } = req.query;
|
|
71
|
+
|
|
72
|
+
if (!walletAddress) {
|
|
73
|
+
return res.status(400).json({ error: 'walletAddress is required' });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = await pool.query(
|
|
77
|
+
'SELECT * FROM device_settings WHERE wallet_address = $1 AND dismissed_a2hs = true',
|
|
78
|
+
[walletAddress]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (result.rows.length > 0) {
|
|
82
|
+
return res.json({
|
|
83
|
+
dismissedA2HS: true,
|
|
84
|
+
lastSeen: result.rows[0].last_seen,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
res.json({
|
|
89
|
+
dismissedA2HS: false,
|
|
90
|
+
lastSeen: null,
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('[DeviceRoutes] Error fetching device settings:', error);
|
|
94
|
+
res.status(500).json({ error: 'Failed to fetch device settings' });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* POST /api/device/dismiss-a2hs
|
|
100
|
+
* Mark "Add to Home Screen" banner as dismissed for this wallet
|
|
101
|
+
*
|
|
102
|
+
* Body: {
|
|
103
|
+
* walletAddress: string (REQUIRED),
|
|
104
|
+
* platform?: string (ios/android),
|
|
105
|
+
* userAgent?: string
|
|
106
|
+
* }
|
|
107
|
+
*/
|
|
108
|
+
router.post('/dismiss-a2hs', async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const { walletAddress, platform, userAgent } = req.body;
|
|
111
|
+
|
|
112
|
+
if (!walletAddress) {
|
|
113
|
+
return res.status(400).json({ error: 'walletAddress is required' });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Upsert: Insert or update if exists
|
|
117
|
+
await pool.query(`
|
|
118
|
+
INSERT INTO device_settings (device_id, wallet_address, dismissed_a2hs, platform, user_agent, last_seen)
|
|
119
|
+
VALUES ($1, $2, true, $3, $4, NOW())
|
|
120
|
+
ON CONFLICT (device_id)
|
|
121
|
+
DO UPDATE SET
|
|
122
|
+
dismissed_a2hs = true,
|
|
123
|
+
platform = COALESCE($3, device_settings.platform),
|
|
124
|
+
user_agent = COALESCE($4, device_settings.user_agent),
|
|
125
|
+
last_seen = NOW()
|
|
126
|
+
`, [`wallet_${walletAddress}`, walletAddress, platform, userAgent]);
|
|
127
|
+
|
|
128
|
+
console.log(`[DeviceRoutes] ✅ A2HS dismissed for wallet: ${walletAddress.slice(0, 8)}...`);
|
|
129
|
+
|
|
130
|
+
res.json({
|
|
131
|
+
success: true,
|
|
132
|
+
dismissedA2HS: true
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('[DeviceRoutes] ❌ Error dismissing A2HS:', error);
|
|
136
|
+
res.status(500).json({ error: 'Failed to save dismissal' });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* POST /api/device/link-wallet
|
|
142
|
+
* Link a wallet address to the current device (by IP)
|
|
143
|
+
* Called when user authenticates - links their wallet to any IP-based dismissals
|
|
144
|
+
*
|
|
145
|
+
* Body: {
|
|
146
|
+
* walletAddress: string
|
|
147
|
+
* }
|
|
148
|
+
*/
|
|
149
|
+
router.post('/link-wallet', async (req, res) => {
|
|
150
|
+
try {
|
|
151
|
+
const { walletAddress } = req.body;
|
|
152
|
+
|
|
153
|
+
if (!walletAddress) {
|
|
154
|
+
return res.status(400).json({ error: 'walletAddress is required' });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const ipAddress = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
|
158
|
+
req.headers['x-real-ip'] ||
|
|
159
|
+
req.connection.remoteAddress ||
|
|
160
|
+
req.socket.remoteAddress;
|
|
161
|
+
|
|
162
|
+
if (!ipAddress) {
|
|
163
|
+
return res.status(400).json({ error: 'Unable to determine IP address' });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const deviceId = `ip_${ipAddress}`;
|
|
167
|
+
|
|
168
|
+
// Update the current IP's record with the wallet address
|
|
169
|
+
await pool.query(`
|
|
170
|
+
UPDATE device_settings
|
|
171
|
+
SET wallet_address = $1, last_seen = NOW()
|
|
172
|
+
WHERE device_id = $2
|
|
173
|
+
`, [walletAddress, deviceId]);
|
|
174
|
+
|
|
175
|
+
console.log(`[DeviceRoutes] ✅ Linked wallet ${walletAddress.slice(0, 8)}... to IP: ${ipAddress}`);
|
|
176
|
+
|
|
177
|
+
res.json({
|
|
178
|
+
success: true,
|
|
179
|
+
walletAddress,
|
|
180
|
+
deviceId
|
|
181
|
+
});
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('[DeviceRoutes] Error linking wallet:', error);
|
|
184
|
+
res.status(500).json({ error: 'Failed to link wallet' });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* DELETE /api/device/:deviceId/dismiss-a2hs
|
|
190
|
+
* Reset dismissal (for testing or user request)
|
|
191
|
+
*/
|
|
192
|
+
router.delete('/:deviceId/dismiss-a2hs', async (req, res) => {
|
|
193
|
+
try {
|
|
194
|
+
const { deviceId } = req.params;
|
|
195
|
+
|
|
196
|
+
await pool.query(
|
|
197
|
+
'UPDATE device_settings SET dismissed_a2hs = false, last_seen = NOW() WHERE device_id = $1',
|
|
198
|
+
[deviceId]
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
console.log(`[DeviceRoutes] ✅ A2HS dismissal reset for device: ${deviceId.slice(0, 8)}...`);
|
|
202
|
+
|
|
203
|
+
res.json({
|
|
204
|
+
success: true,
|
|
205
|
+
deviceId,
|
|
206
|
+
dismissedA2HS: false
|
|
207
|
+
});
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('[DeviceRoutes] Error resetting dismissal:', error);
|
|
210
|
+
res.status(500).json({ error: 'Failed to reset dismissal' });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
module.exports = router;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 💬 Direct Message Routes
|
|
3
|
+
*
|
|
4
|
+
* REST API endpoints for direct messages
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const express = require('express');
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
const { authenticate } = require('../middleware/authenticate');
|
|
10
|
+
|
|
11
|
+
// Will be injected from server.js
|
|
12
|
+
let dmService = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize routes with dependencies
|
|
16
|
+
*/
|
|
17
|
+
function initializeDMRoutes(dmServiceInstance) {
|
|
18
|
+
dmService = dmServiceInstance;
|
|
19
|
+
return router;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* GET /dm/conversations
|
|
24
|
+
* Get all conversations for the current user (inbox)
|
|
25
|
+
*/
|
|
26
|
+
router.get('/conversations', authenticate, async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const userId = req.user.userId;
|
|
29
|
+
const limit = parseInt(req.query.limit) || 20;
|
|
30
|
+
|
|
31
|
+
const conversations = await dmService.getConversations(userId, limit);
|
|
32
|
+
res.json({ conversations });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('[DM Routes] Error getting conversations:', error);
|
|
35
|
+
res.status(500).json({ error: 'Failed to get conversations' });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* GET /dm/conversation/:walletAddress
|
|
41
|
+
* Get conversation history with a specific user
|
|
42
|
+
*/
|
|
43
|
+
router.get('/conversation/:walletAddress', authenticate, async (req, res) => {
|
|
44
|
+
try {
|
|
45
|
+
const userId = req.user.userId;
|
|
46
|
+
const { walletAddress } = req.params;
|
|
47
|
+
const limit = parseInt(req.query.limit) || 50;
|
|
48
|
+
const beforeId = req.query.beforeId ? parseInt(req.query.beforeId) : null;
|
|
49
|
+
|
|
50
|
+
// Get other user's ID from wallet
|
|
51
|
+
const otherUser = await dmService.getUserByWallet(walletAddress);
|
|
52
|
+
if (!otherUser) {
|
|
53
|
+
return res.status(404).json({ error: 'User not found' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const messages = await dmService.getConversation(userId, otherUser.id, limit, beforeId);
|
|
57
|
+
|
|
58
|
+
// Mark messages as read
|
|
59
|
+
await dmService.markAsRead(userId, otherUser.id);
|
|
60
|
+
|
|
61
|
+
res.json({
|
|
62
|
+
messages,
|
|
63
|
+
otherUser: {
|
|
64
|
+
id: otherUser.id,
|
|
65
|
+
username: otherUser.username,
|
|
66
|
+
avatar: otherUser.avatar,
|
|
67
|
+
walletAddress: otherUser.walletAddress,
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('[DM Routes] Error getting conversation:', error);
|
|
72
|
+
res.status(500).json({ error: 'Failed to get conversation' });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* POST /dm/send
|
|
78
|
+
* Send a direct message
|
|
79
|
+
*/
|
|
80
|
+
router.post('/send', authenticate, async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const senderId = req.user.userId;
|
|
83
|
+
const { recipientWallet, message } = req.body;
|
|
84
|
+
|
|
85
|
+
if (!recipientWallet || !message) {
|
|
86
|
+
return res.status(400).json({ error: 'recipientWallet and message are required' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Get recipient's ID from wallet
|
|
90
|
+
const recipient = await dmService.getUserByWallet(recipientWallet);
|
|
91
|
+
if (!recipient) {
|
|
92
|
+
return res.status(404).json({ error: 'Recipient not found' });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (recipient.id === senderId) {
|
|
96
|
+
return res.status(400).json({ error: 'Cannot send message to yourself' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dm = await dmService.sendMessage(senderId, recipient.id, message);
|
|
100
|
+
res.json({ message: dm });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error('[DM Routes] Error sending message:', error);
|
|
103
|
+
res.status(500).json({ error: error.message || 'Failed to send message' });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* POST /dm/read/:walletAddress
|
|
109
|
+
* Mark all messages from a user as read
|
|
110
|
+
*/
|
|
111
|
+
router.post('/read/:walletAddress', authenticate, async (req, res) => {
|
|
112
|
+
try {
|
|
113
|
+
const userId = req.user.userId;
|
|
114
|
+
const { walletAddress } = req.params;
|
|
115
|
+
|
|
116
|
+
const otherUser = await dmService.getUserByWallet(walletAddress);
|
|
117
|
+
if (!otherUser) {
|
|
118
|
+
return res.status(404).json({ error: 'User not found' });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const count = await dmService.markAsRead(userId, otherUser.id);
|
|
122
|
+
res.json({ markedRead: count });
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('[DM Routes] Error marking as read:', error);
|
|
125
|
+
res.status(500).json({ error: 'Failed to mark as read' });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* GET /dm/unread
|
|
131
|
+
* Get unread DM count
|
|
132
|
+
*/
|
|
133
|
+
router.get('/unread', authenticate, async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
const userId = req.user.userId;
|
|
136
|
+
const count = await dmService.getUnreadCount(userId);
|
|
137
|
+
res.json({ unreadCount: count });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('[DM Routes] Error getting unread count:', error);
|
|
140
|
+
res.status(500).json({ error: 'Failed to get unread count' });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* DELETE /dm/conversation/:walletAddress
|
|
146
|
+
* Delete a conversation (soft delete for current user only)
|
|
147
|
+
*/
|
|
148
|
+
router.delete('/conversation/:walletAddress', authenticate, async (req, res) => {
|
|
149
|
+
try {
|
|
150
|
+
const userId = req.user.userId;
|
|
151
|
+
const { walletAddress } = req.params;
|
|
152
|
+
|
|
153
|
+
const otherUser = await dmService.getUserByWallet(walletAddress);
|
|
154
|
+
if (!otherUser) {
|
|
155
|
+
return res.status(404).json({ error: 'User not found' });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await dmService.deleteConversation(userId, otherUser.id);
|
|
159
|
+
res.json({ success: true });
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('[DM Routes] Error deleting conversation:', error);
|
|
162
|
+
res.status(500).json({ error: 'Failed to delete conversation' });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
module.exports = { initializeDMRoutes, router };
|
|
167
|
+
|