aaaa
This commit is contained in:
parent
cb9ebad0fa
commit
2479564b8b
|
|
@ -224,7 +224,13 @@ export class GatewayManager implements DurableObject {
|
||||||
token: this.env.DISCORD_BOT_TOKEN,
|
token: this.env.DISCORD_BOT_TOKEN,
|
||||||
intents: INTENTS,
|
intents: INTENTS,
|
||||||
properties: { os: "linux", browser: "dough-restful", device: "dough-restful" },
|
properties: { os: "linux", browser: "dough-restful", device: "dough-restful" },
|
||||||
presence: { status: "invisible", activities: [], since: 0, afk: false },
|
presence: {
|
||||||
|
status: "idle",
|
||||||
|
afk: false,
|
||||||
|
since: 0,
|
||||||
|
// Custom status (type 4): the text shown is the `state` field.
|
||||||
|
activities: [{ name: "Custom Status", type: 4, state: "meow meow mrrp meow" }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub === "profile") {
|
if (sub === "profile") {
|
||||||
const profile = await getProfile(env, id);
|
const profile = await getProfile(env, id, ctx);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return json({ success: false, error: { code: "not_found", message: "User not found." } }, 404);
|
return json({ success: false, error: { code: "not_found", message: "User not found." } }, 404);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unified record: profile (REST) + presence (gateway), in parallel.
|
// Unified record: profile (REST) + presence (gateway), in parallel.
|
||||||
const [profile, presence] = await Promise.all([getProfile(env, id), fetchPresence(env, id)]);
|
const [profile, presence] = await Promise.all([getProfile(env, id, ctx), fetchPresence(env, id)]);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return json({ success: false, error: { code: "not_found", message: "User not found." } }, 404);
|
return json({ success: false, error: { code: "not_found", message: "User not found." } }, 404);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,18 +84,43 @@ function cacheKey(id: string): string {
|
||||||
return `profile:${id}`;
|
return `profile:${id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build profile from Discord, with a KV read-through cache. */
|
/**
|
||||||
export async function getProfile(env: Env, id: string): Promise<ProfileResult | null> {
|
* Build profile from Discord — LIVE-FIRST.
|
||||||
|
*
|
||||||
|
* Always fetches fresh from Discord so profile/badges/connections are current,
|
||||||
|
* and only falls back to the KV copy if the REST call fails (e.g. Discord is
|
||||||
|
* rate-limiting us). The KV copy is refreshed in the background, throttled to
|
||||||
|
* at most one write per TTL window so we don't blow KV write limits.
|
||||||
|
*/
|
||||||
|
export async function getProfile(
|
||||||
|
env: Env,
|
||||||
|
id: string,
|
||||||
|
ctx?: ExecutionContext
|
||||||
|
): Promise<ProfileResult | null> {
|
||||||
|
const fresh = await buildFreshProfile(env, id);
|
||||||
|
if (fresh) {
|
||||||
|
const refresh = maybeRefreshCache(env, id, fresh);
|
||||||
|
if (ctx) ctx.waitUntil(refresh);
|
||||||
|
else await refresh;
|
||||||
|
return fresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discord REST failed — serve last-known-good from KV if we have it.
|
||||||
const cached = await env.PROFILE_CACHE.get(cacheKey(id), "json");
|
const cached = await env.PROFILE_CACHE.get(cacheKey(id), "json");
|
||||||
if (cached) {
|
if (cached) {
|
||||||
const c = cached as Omit<ProfileResult, "source">;
|
const c = cached as Omit<ProfileResult, "source">;
|
||||||
return { ...c, source: "cache" };
|
return { ...c, source: "cache" };
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await buildFreshProfile(env, id);
|
/** Write the fallback copy to KV, but at most once per TTL window. */
|
||||||
if (!result) return null;
|
async function maybeRefreshCache(env: Env, id: string, result: ProfileResult): Promise<void> {
|
||||||
|
|
||||||
const ttl = Math.max(60, Number(env.PROFILE_CACHE_TTL_SECONDS || "300"));
|
const ttl = Math.max(60, Number(env.PROFILE_CACHE_TTL_SECONDS || "300"));
|
||||||
|
const { metadata } = await env.PROFILE_CACHE.getWithMetadata(cacheKey(id));
|
||||||
|
const lastWrite = (metadata as { t?: number } | null)?.t ?? 0;
|
||||||
|
if (Date.now() - lastWrite < ttl * 1000) return; // throttled
|
||||||
|
|
||||||
await env.PROFILE_CACHE.put(
|
await env.PROFILE_CACHE.put(
|
||||||
cacheKey(id),
|
cacheKey(id),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|
@ -103,9 +128,8 @@ export async function getProfile(env: Env, id: string): Promise<ProfileResult |
|
||||||
badges: result.badges,
|
badges: result.badges,
|
||||||
connected_accounts: result.connected_accounts,
|
connected_accounts: result.connected_accounts,
|
||||||
}),
|
}),
|
||||||
{ expirationTtl: ttl }
|
{ expirationTtl: ttl * 2, metadata: { t: Date.now() } }
|
||||||
);
|
);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildFreshProfile(env: Env, id: string): Promise<ProfileResult | null> {
|
async function buildFreshProfile(env: Env, id: string): Promise<ProfileResult | null> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue