+30
-14
dist/index.html
+30
-14
dist/index.html
···
1
-
<!DOCTYPE html>
2
-
<html lang="en">
3
-
<head>
4
-
<meta charset="UTF-8" />
5
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
-
<title>ATLast: Sync Your TikTok Follows → ATmosphere (Skylight, Bluesky, etc.)</title>
8
-
<script type="module" crossorigin src="/assets/index-8vZ0WHUG.js"></script>
9
-
<link rel="stylesheet" crossorigin href="/assets/index-Bs4vTtm8.css">
10
-
</head>
11
-
<body>
12
-
<div id="root"></div>
13
-
</body>
14
-
</html>
1
+
<!doctype html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8" />
5
+
<link rel="manifest" href="/site.webmanifest" />
6
+
<link
7
+
rel="apple-touch-icon"
8
+
sizes="180x180"
9
+
href="/apple-touch-icon.png"
10
+
/>
11
+
<link
12
+
rel="icon"
13
+
type="image/x-icon"
14
+
sizes="32x32"
15
+
href="/favicon.ico"
16
+
/>
17
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
18
+
19
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
20
+
<title>
21
+
ATLast: Sync Your TikTok Follows → ATmosphere (Skylight, Bluesky,
22
+
etc.)
23
+
</title>
24
+
<script type="module" crossorigin src="/assets/index-Dx_AzG_Q.js"></script>
25
+
<link rel="stylesheet" crossorigin href="/assets/index-C69tQ_4S.css">
26
+
</head>
27
+
<body>
28
+
<div id="root"></div>
29
+
</body>
30
+
</html>
+4
netlify/functions/logout.ts
+4
netlify/functions/logout.ts
···
27
27
console.log("[logout] Session ID from cookie:", sessionId);
28
28
29
29
if (sessionId) {
30
+
// Get the DID before deleting
31
+
const userSession = await userSessions.get(sessionId);
32
+
const did = userSession?.did;
33
+
30
34
// Delete session from database
31
35
await userSessions.del(sessionId);
32
36
console.log("[logout] Deleted session from database");
+14
-67
netlify/functions/session.ts
+14
-67
netlify/functions/session.ts
···
16
16
return key;
17
17
}
18
18
19
-
// ENHANCED: Two-tier cache system
20
-
// Tier 1: In-memory cache for profile data (lives for function instance)
19
+
// In-memory cache for profile
21
20
const profileCache = new Map<string, { data: any; timestamp: number }>();
22
21
const PROFILE_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
23
-
24
-
// Tier 2: Session metadata cache (DID -> basic info, faster than full OAuth restore)
25
-
const sessionMetadataCache = new Map<
26
-
string,
27
-
{
28
-
did: string;
29
-
lastSeen: number;
30
-
profileFetchNeeded: boolean;
31
-
}
32
-
>();
33
22
34
23
export const handler: Handler = async (
35
24
event: HandlerEvent,
···
49
38
};
50
39
}
51
40
52
-
// OPTIMIZATION: Check session metadata cache first (avoids DB query)
53
-
const cachedMetadata = sessionMetadataCache.get(sessionId);
54
-
const now = Date.now();
41
+
// Check database for session
42
+
const userSession = await userSessions.get(sessionId);
55
43
56
-
let did: string;
44
+
if (!userSession) {
45
+
return {
46
+
statusCode: 401,
47
+
headers: { "Content-Type": "application/json" },
48
+
body: JSON.stringify({ error: "Invalid or expired session" }),
49
+
};
50
+
}
57
51
58
-
if (cachedMetadata && now - cachedMetadata.lastSeen < 60000) {
59
-
// Session seen within last minute, trust the cache
60
-
did = cachedMetadata.did;
61
-
console.log("Session metadata from cache");
62
-
} else {
63
-
// Need to verify session from database
64
-
const userSession = await userSessions.get(sessionId);
65
-
if (!userSession) {
66
-
// Clear stale cache entry
67
-
sessionMetadataCache.delete(sessionId);
68
-
return {
69
-
statusCode: 401,
70
-
headers: { "Content-Type": "application/json" },
71
-
body: JSON.stringify({ error: "Invalid or expired session" }),
72
-
};
73
-
}
52
+
const did = userSession.did;
53
+
const now = Date.now();
74
54
75
-
did = userSession.did;
76
-
77
-
// Update session metadata cache
78
-
sessionMetadataCache.set(sessionId, {
79
-
did,
80
-
lastSeen: now,
81
-
profileFetchNeeded: true,
82
-
});
83
-
84
-
// Cleanup: Remove old session metadata entries
85
-
if (sessionMetadataCache.size > 200) {
86
-
for (const [sid, meta] of sessionMetadataCache.entries()) {
87
-
if (now - meta.lastSeen > 300000) {
88
-
// 5 minutes
89
-
sessionMetadataCache.delete(sid);
90
-
}
91
-
}
92
-
}
93
-
}
94
-
95
-
// Check profile cache (Tier 1)
55
+
// Check profile cache
96
56
const cached = profileCache.get(did);
97
57
if (cached && now - cached.timestamp < PROFILE_CACHE_TTL) {
98
58
console.log("Returning cached profile for", did);
99
-
100
-
// Update session metadata last seen
101
-
const meta = sessionMetadataCache.get(sessionId);
102
-
if (meta) {
103
-
meta.lastSeen = now;
104
-
}
105
59
106
60
return {
107
61
statusCode: 200,
···
181
135
description: profile.data.description,
182
136
};
183
137
184
-
// Cache the profile data (Tier 1)
138
+
// Cache the profile data
185
139
profileCache.set(did, {
186
140
data: profileData,
187
141
timestamp: now,
188
142
});
189
-
190
-
// Update session metadata (Tier 2)
191
-
const meta = sessionMetadataCache.get(sessionId);
192
-
if (meta) {
193
-
meta.lastSeen = now;
194
-
meta.profileFetchNeeded = false;
195
-
}
196
143
197
144
// Clean up old profile cache entries
198
145
if (profileCache.size > 100) {
+14
-13
package-lock.json
+14
-13
package-lock.json
···
30
30
"@types/react-dom": "^19.1.9",
31
31
"@vitejs/plugin-react": "^4.2.1",
32
32
"autoprefixer": "^10.4.21",
33
+
"baseline-browser-mapping": "^2.8.31",
33
34
"gh-pages": "^6.3.0",
34
35
"postcss": "^8.5.6",
35
36
"tailwindcss": "^3.4.0",
···
3408
3409
"license": "MIT"
3409
3410
},
3410
3411
"node_modules/baseline-browser-mapping": {
3411
-
"version": "2.8.7",
3412
-
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.7.tgz",
3413
-
"integrity": "sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==",
3412
+
"version": "2.8.31",
3413
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz",
3414
+
"integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==",
3414
3415
"dev": true,
3415
3416
"license": "Apache-2.0",
3416
3417
"bin": {
···
4944
4945
}
4945
4946
},
4946
4947
"node_modules/glob": {
4947
-
"version": "10.4.5",
4948
-
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
4949
-
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
4948
+
"version": "10.5.0",
4949
+
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
4950
+
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
4950
4951
"license": "ISC",
4951
4952
"dependencies": {
4952
4953
"foreground-child": "^3.1.0",
···
7266
7267
}
7267
7268
},
7268
7269
"node_modules/tar": {
7269
-
"version": "7.5.1",
7270
-
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz",
7271
-
"integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==",
7272
-
"license": "ISC",
7270
+
"version": "7.5.2",
7271
+
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz",
7272
+
"integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==",
7273
+
"license": "BlueOak-1.0.0",
7273
7274
"dependencies": {
7274
7275
"@isaacs/fs-minipass": "^4.0.0",
7275
7276
"chownr": "^3.0.0",
···
7628
7629
}
7629
7630
},
7630
7631
"node_modules/vite": {
7631
-
"version": "5.4.20",
7632
-
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz",
7633
-
"integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==",
7632
+
"version": "5.4.21",
7633
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
7634
+
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
7634
7635
"dev": true,
7635
7636
"license": "MIT",
7636
7637
"peer": true,