diff --git a/README.md b/README.md index c29aca2..f03e966 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,9 @@ http://cachet.dunkirk.sh { ### Usage -The api is pretty simple. You can get a profile picture by calling `GET /profile/:id` where `:id` is the slack user id. You can get an emoji by calling `GET /emoji/:name` where `:name` is the name of the emoji. You can also get a list of all emojis by calling `GET /emojis`. +The api is pretty simple. You can get a profile picture by calling `GET /users/:id` where `:id` is the slack user id. You can get a redirect to the profile image directly with `GET /users/:id/r`. You can get an emoji by calling `GET /emoji/:name` where `:name` is the name of the emoji. You can also get a list of all emojis by calling `GET /emojis`. + +On cache miss, `GET /users/:id` returns `202 Accepted` with a placeholder image URL and queues a background fetch. Retry after a few seconds to get the real profile data. Additionally, you can manually purge a specific user's cache with `POST /users/:user/purge` (requires authentication with a bearer token). diff --git a/src/handlers/index.ts b/src/handlers/index.ts index d7e73a3..05efba6 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -5,7 +5,6 @@ // These will be injected by the route system import type { SlackCache } from "../cache"; import type { RouteHandlerWithAnalytics } from "../lib/analytics-wrapper"; -import type { SlackUser } from "../slack"; import type { SlackWrapper } from "../slackWrapper"; let cache!: SlackCache; @@ -72,40 +71,19 @@ export const handleGetUser: RouteHandlerWithAnalytics = async ( const userId = url.pathname.split("/").pop() || ""; const user = await cache.getUser(userId); - if (!user || !user.imageUrl) { - let slackUser: SlackUser; - try { - slackUser = await slackApp.getUserInfo(userId); - } catch (e) { - if (e instanceof Error && e.message === "user_not_found") { - recordAnalytics(404); - return Response.json({ message: "User not found" }, { status: 404 }); - } - - recordAnalytics(500); - return Response.json( - { message: "Internal server error" }, - { status: 500 }, - ); - } - - const resolvedId = userId.toUpperCase(); - await cache.insertUser( - resolvedId, - slackUser.real_name || slackUser.name || "Unknown", - slackUser.profile?.pronouns || "", - slackUser.profile?.image_512 || slackUser.profile?.image_192 || "", + if (!user?.imageUrl) { + cache.queueUserUpdate(userId); + recordAnalytics(202); + return Response.json( + { + id: userId.toUpperCase(), + userId: userId.toUpperCase(), + displayName: "Unknown", + pronouns: "", + imageUrl: "https://l4.dunkirk.sh/i/5DjfoBI58Pfw.webp", + }, + { status: 202 }, ); - - recordAnalytics(200); - return Response.json({ - id: resolvedId, - userId: resolvedId, - displayName: slackUser.real_name || slackUser.name || "Unknown", - pronouns: slackUser.profile?.pronouns || "", - imageUrl: - slackUser.profile?.image_512 || slackUser.profile?.image_192 || "", - }); } recordAnalytics(200); diff --git a/src/routes/api-routes.ts b/src/routes/api-routes.ts index 1d8d883..7c466b7 100644 --- a/src/routes/api-routes.ts +++ b/src/routes/api-routes.ts @@ -145,7 +145,19 @@ export function createApiRoutes(cache: SlackCache, slackApp: SlackWrapper) { }, }, }), - apiResponse(404, "User not found"), + apiResponse(202, "User not yet cached; background fetch queued. Returns placeholder image.", { + type: "object", + properties: { + id: { type: "string", example: "U062UG485EE" }, + userId: { type: "string", example: "U062UG485EE" }, + displayName: { type: "string", example: "Unknown" }, + pronouns: { type: "string", example: "" }, + imageUrl: { + type: "string", + example: "https://l4.dunkirk.sh/i/5DjfoBI58Pfw.webp", + }, + }, + }), ]), }, ),