tangled
alpha
login
or
join now
dollspace.gay
/
Aurora-Prism
4
fork
atom
A third party ATProto appview
4
fork
atom
overview
issues
pulls
pipelines
display fixes
dollspacegay.tngl.sh
4 months ago
a42bfe53
8bcb59e4
+92
-25
4 changed files
expand all
collapse all
unified
split
.claude
settings.local.json
server
routes.ts
services
redis-queue.ts
storage.ts
+3
-1
.claude/settings.local.json
···
18
18
"Bash(yarn install)",
19
19
"Bash(node --version:*)",
20
20
"Bash(npm:*)",
21
21
-
"Bash(docker-compose restart:*)"
21
21
+
"Bash(docker-compose restart:*)",
22
22
+
"Bash(echo \"# Bluesky Branding Removal Checklist\n\n## 1. App Icons & Images\n- assets/app-icons/*.png - App icons (replace with Aurora Prism branding)\n- assets/favicon.png - Browser favicon\n- assets/icon-android-*.png - Android icons\n- assets/default-avatar.png - Default avatar image\n\n## 2. App Metadata\n- app.json - App name, slug, description\n- package.json - App name and description\n\n## 3. Text References (276 occurrences)\n- Onboarding screens (src/screens/Onboarding/)\n- Signup screens (src/screens/Signup/)\n- Settings/About screens\n- Terms of Service / Privacy Policy references\n- Help text and tooltips\n- Error messages mentioning Bluesky\n\n## 4. URLs\n- bsky.app references (feed URLs, profile URLs)\n- bsky.social references\n- Links to Bluesky support/help\n\n## 5. Service Names\n- Bluesky Moderation Service references\n- Default feed generator names\n\nTotal: 276 text references found\")",
23
23
+
"Bash(psql \"$DATABASE_URL\" -c \"SELECT \n (SELECT COUNT(*) FROM users) as users,\n (SELECT COUNT(*) FROM posts) as posts,\n (SELECT COUNT(*) FROM likes) as likes,\n (SELECT COUNT(*) FROM reposts) as reposts,\n (SELECT COUNT(*) FROM follows) as follows,\n (SELECT COUNT(*) FROM blocks) as blocks;\")"
22
24
],
23
25
"deny": [],
24
26
"ask": []
+20
server/routes.ts
···
4323
4323
});
4324
4324
});
4325
4325
4326
4326
+
// Force stats refresh endpoint - clears cache and recalculates from database
4327
4327
+
app.post('/api/metrics/refresh', async (_req, res) => {
4328
4328
+
try {
4329
4329
+
// Clear the stats cache to force a fresh calculation
4330
4330
+
storage.clearStatsCache();
4331
4331
+
4332
4332
+
// Get fresh stats (will query database)
4333
4333
+
const stats = await storage.getStats();
4334
4334
+
4335
4335
+
res.json({
4336
4336
+
success: true,
4337
4337
+
message: 'Stats refreshed successfully',
4338
4338
+
stats
4339
4339
+
});
4340
4340
+
} catch (err) {
4341
4341
+
console.error('[METRICS] Error refreshing stats:', err);
4342
4342
+
res.status(500).json({ error: 'InternalServerError', message: 'Failed to refresh stats' });
4343
4343
+
}
4344
4344
+
});
4345
4345
+
4326
4346
// Minimal dead-letter inspection endpoint (admin only in production)
4327
4347
app.get('/api/redis/dead-letters', async (_req, res) => {
4328
4348
try {
+12
server/services/redis-queue.ts
···
760
760
}
761
761
}
762
762
763
763
+
async setRecordCount(table: string, value: number) {
764
764
+
if (!this.redis || !this.isInitialized) {
765
765
+
return;
766
766
+
}
767
767
+
768
768
+
try {
769
769
+
await this.redis.hset('db:record_counts', table, value.toString());
770
770
+
} catch (error) {
771
771
+
console.error('[REDIS] Error setting record count:', error);
772
772
+
}
773
773
+
}
774
774
+
763
775
async getRecordCounts(): Promise<Record<string, number>> {
764
776
if (!this.redis || !this.isInitialized) {
765
777
return {};
+57
-24
server/storage.ts
···
3864
3864
});
3865
3865
}
3866
3866
3867
3867
+
clearStatsCache() {
3868
3868
+
console.log('[STORAGE] Clearing stats cache');
3869
3869
+
this.statsCache = null;
3870
3870
+
this.statsQueryInProgress = false;
3871
3871
+
}
3872
3872
+
3867
3873
private async refreshStatsInBackground() {
3868
3874
// Skip if query is already in progress
3869
3875
if (this.statsQueryInProgress) {
···
3907
3913
) {
3908
3914
this.refreshStatsInBackground();
3909
3915
}
3916
3916
+
console.log('[STORAGE] Returning cached stats:', this.statsCache.data);
3910
3917
return this.statsCache.data;
3911
3918
}
3912
3919
3913
3920
// No cache yet - try Redis counters first (fast)
3914
3921
const { redisQueue } = await import('./services/redis-queue');
3915
3922
const redisCounts = await redisQueue.getRecordCounts();
3923
3923
+
3924
3924
+
console.log('[STORAGE] Redis counts:', redisCounts);
3916
3925
3917
3926
if (Object.keys(redisCounts).length > 0) {
3918
3927
const data = {
···
3925
3934
};
3926
3935
// Cache it
3927
3936
this.statsCache = { data, timestamp: Date.now() };
3937
3937
+
console.log('[STORAGE] Using Redis counts:', data);
3928
3938
return data;
3929
3939
}
3930
3940
3941
3941
+
console.log('[STORAGE] No Redis counts, falling back to PostgreSQL COUNT queries');
3942
3942
+
3931
3943
// No Redis counts - fallback to PostgreSQL query (only on first load)
3932
3944
if (this.statsQueryInProgress) {
3933
3945
// Another request is already fetching, return zeros
···
3944
3956
this.statsQueryInProgress = true;
3945
3957
3946
3958
try {
3947
3947
-
// Add 5 second timeout to prevent blocking
3959
3959
+
// Use accurate COUNT(*) queries instead of pg_stat_user_tables estimates
3960
3960
+
// Run all counts in a single query for better performance
3948
3961
const statsPromise = this.db.execute<{
3949
3949
-
schemaname: string;
3950
3950
-
relname: string;
3951
3951
-
count: number;
3962
3962
+
users: string;
3963
3963
+
posts: string;
3964
3964
+
likes: string;
3965
3965
+
reposts: string;
3966
3966
+
follows: string;
3967
3967
+
blocks: string;
3952
3968
}>(sql`
3953
3953
-
SELECT
3954
3954
-
schemaname,
3955
3955
-
relname,
3956
3956
-
n_live_tup as count
3957
3957
-
FROM pg_stat_user_tables
3958
3958
-
WHERE schemaname = 'public'
3959
3959
-
AND relname IN ('users', 'posts', 'likes', 'reposts', 'follows', 'blocks')
3969
3969
+
SELECT
3970
3970
+
(SELECT COUNT(*)::text FROM users) as users,
3971
3971
+
(SELECT COUNT(*)::text FROM posts) as posts,
3972
3972
+
(SELECT COUNT(*)::text FROM likes) as likes,
3973
3973
+
(SELECT COUNT(*)::text FROM reposts) as reposts,
3974
3974
+
(SELECT COUNT(*)::text FROM follows) as follows,
3975
3975
+
(SELECT COUNT(*)::text FROM blocks) as blocks
3960
3976
`);
3961
3977
3962
3978
const timeoutPromise = new Promise<never>((_, reject) => {
3963
3963
-
setTimeout(() => reject(new Error('Stats query timeout')), 5000);
3979
3979
+
setTimeout(() => reject(new Error('Stats query timeout')), 10000);
3964
3980
});
3965
3981
3966
3982
const result = await Promise.race([statsPromise, timeoutPromise]);
3967
3983
3968
3968
-
const stats: Record<string, number> = {};
3969
3969
-
result.rows.forEach(
3970
3970
-
(row: { relname: string; count: string | number }) => {
3971
3971
-
stats[row.relname] = Number(row.count || 0);
3972
3972
-
}
3973
3973
-
);
3984
3984
+
const row = result.rows[0] as {
3985
3985
+
users?: string;
3986
3986
+
posts?: string;
3987
3987
+
likes?: string;
3988
3988
+
reposts?: string;
3989
3989
+
follows?: string;
3990
3990
+
blocks?: string;
3991
3991
+
};
3974
3992
3975
3993
const data = {
3976
3976
-
totalUsers: stats.users || 0,
3977
3977
-
totalPosts: stats.posts || 0,
3978
3978
-
totalLikes: stats.likes || 0,
3979
3979
-
totalReposts: stats.reposts || 0,
3980
3980
-
totalFollows: stats.follows || 0,
3981
3981
-
totalBlocks: stats.blocks || 0,
3994
3994
+
totalUsers: parseInt(row.users || '0'),
3995
3995
+
totalPosts: parseInt(row.posts || '0'),
3996
3996
+
totalLikes: parseInt(row.likes || '0'),
3997
3997
+
totalReposts: parseInt(row.reposts || '0'),
3998
3998
+
totalFollows: parseInt(row.follows || '0'),
3999
3999
+
totalBlocks: parseInt(row.blocks || '0'),
3982
4000
};
3983
4001
4002
4002
+
console.log('[STORAGE] PostgreSQL COUNT query result:', data);
4003
4003
+
3984
4004
// Cache the result
3985
4005
this.statsCache = { data, timestamp: Date.now() };
3986
4006
this.statsQueryInProgress = false;
4007
4007
+
4008
4008
+
// Update Redis counters with accurate counts
4009
4009
+
const { redisQueue: redisQueueUpdate } = await import('./services/redis-queue');
4010
4010
+
await Promise.all([
4011
4011
+
redisQueueUpdate.setRecordCount('users', data.totalUsers),
4012
4012
+
redisQueueUpdate.setRecordCount('posts', data.totalPosts),
4013
4013
+
redisQueueUpdate.setRecordCount('likes', data.totalLikes),
4014
4014
+
redisQueueUpdate.setRecordCount('reposts', data.totalReposts),
4015
4015
+
redisQueueUpdate.setRecordCount('follows', data.totalFollows),
4016
4016
+
redisQueueUpdate.setRecordCount('blocks', data.totalBlocks),
4017
4017
+
]).catch((err) => {
4018
4018
+
console.error('[STORAGE] Failed to update Redis counters:', err);
4019
4019
+
});
3987
4020
3988
4021
return data;
3989
4022
} catch (error) {