···
import { SlackCache } from "./cache";
import { SlackWrapper } from "./slackWrapper";
import { getEmojiUrl } from "../utils/emojiHelper";
-
import type { SlackUser } from "./slack";
-
import swaggerSpec from "./swagger";
import dashboard from "./dashboard.html";
import swagger from "./swagger.html";
···
environment: process.env.NODE_ENV,
dsn: process.env.SENTRY_DSN,
-
// Ignore all 404-related errors
console.warn("Sentry DSN not provided, error monitoring is disabled");
···
const emojiEntries = Object.entries(emojis)
if (typeof url === "string" && url.startsWith("alias:")) {
-
const aliasName = url.substring(6); // Remove 'alias:' prefix
const aliasUrl = emojis[aliasName] ?? getEmojiUrl(aliasName) ?? null;
···
// Inject SlackWrapper into cache for background user updates
cache.setSlackWrapper(slackApp);
-
// Cache maintenance is now handled automatically by cache.ts scheduled tasks
-
"/dashboard": dashboard,
-
"/swagger.json": async (request) => {
-
return Response.json(swaggerSpec);
-
"/favicon.ico": async (request) => {
-
return new Response(Bun.file("./favicon.ico"));
-
// Root route - redirect to dashboard for browsers
-
"/": async (request) => {
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
const userAgent = request.headers.get("user-agent") || "";
-
userAgent.toLowerCase().includes("mozilla") ||
-
userAgent.toLowerCase().includes("chrome") ||
-
userAgent.toLowerCase().includes("safari")
-
return new Response(null, {
-
headers: { Location: "/dashboard" },
-
"Hello World from Cachet 😊\n\n---\nSee /swagger for docs\nSee /dashboard for analytics\n---",
-
// Health check endpoint
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleHealthCheck(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleGetUser(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleUserRedirect(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handlePurgeUser(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleListEmojis(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleGetEmoji(request, recordAnalytics);
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleEmojiRedirect(request, recordAnalytics);
-
// Reset cache endpoint
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleResetCache(request, recordAnalytics);
-
// Fast essential stats endpoint - loads immediately
-
"/api/stats/essential": {
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
"/api/stats/essential",
-
Date.now() - startTime,
-
return handleGetEssentialStats(request, recordAnalytics);
-
// Chart data endpoint - loads after essential stats
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleGetChartData(request, recordAnalytics);
-
// User agents endpoint - loads last
-
"/api/stats/useragents": {
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
"/api/stats/useragents",
-
Date.now() - startTime,
-
return handleGetUserAgents(request, recordAnalytics);
-
// Original stats endpoint (for backwards compatibility)
-
const startTime = Date.now();
-
const recordAnalytics = async (statusCode: number) => {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return handleGetStats(request, recordAnalytics);
-
// Enable development mode for hot reloading
-
// Fallback fetch handler for unmatched routes and error handling
-
const url = new URL(request.url);
-
const path = url.pathname;
-
const method = request.method;
-
const startTime = Date.now();
-
// Record request analytics (except for favicon and swagger)
-
const recordAnalytics = async (statusCode: number) => {
-
if (path !== "/favicon.ico" && !path.startsWith("/swagger")) {
-
const userAgent = request.headers.get("user-agent") || "";
-
request.headers.get("x-forwarded-for") ||
-
request.headers.get("x-real-ip") ||
-
await cache.recordRequest(
-
Date.now() - startTime,
-
return new Response("Not Found", { status: 404 });
-
`\x1b[31m x\x1b[0m unhandled error: \x1b[31m${error instanceof Error ? error.message : String(error)}\x1b[0m`,
-
// Don't send 404 errors to Sentry
-
error instanceof Error &&
-
(error.message === "Not Found" ||
-
error.message === "user_not_found" ||
-
error.message === "emoji_not_found");
-
if (!is404 && error instanceof Error) {
-
Sentry.withScope((scope) => {
-
scope.setExtra("url", request.url);
-
Sentry.captureException(error);
-
return new Response("Internal Server Error", { status: 500 });
-
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
-
`\n---\n\n🐰 Bun server is running at ${server.url} on ${process.env.NODE_ENV}\n\n---\n`,
-
async function handleHealthCheck(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const slackConnection = await slackApp.testAuth();
-
const databaseConnection = await cache.healthCheck();
-
if (!slackConnection || !databaseConnection) {
-
await recordAnalytics(500);
-
slack: slackConnection,
-
database: databaseConnection,
-
await recordAnalytics(200);
-
async function handleGetUser(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const userId = url.pathname.split("/").pop() || "";
-
const user = await cache.getUser(userId);
-
// If not found then check slack first
-
if (!user || !user.imageUrl) {
-
let slackUser: SlackUser;
-
slackUser = await slackApp.getUserInfo(userId);
-
if (e instanceof Error && e.message === "user_not_found") {
-
await recordAnalytics(404);
-
return Response.json({ message: "User not found" }, { status: 404 });
-
Sentry.withScope((scope) => {
-
scope.setExtra("url", request.url);
-
scope.setExtra("user", userId);
-
Sentry.captureException(e);
-
if (e instanceof Error)
-
`\x1b[38;5;214m ⚠️ WARN\x1b[0m error on fetching user from slack: \x1b[38;5;208m${e.message}\x1b[0m`,
-
await recordAnalytics(500);
-
{ message: `Error fetching user from Slack: ${e}` },
-
slackUser.profile.display_name_normalized ||
-
slackUser.profile.real_name_normalized;
-
await cache.insertUser(
-
slackUser.profile.pronouns,
-
slackUser.profile.image_512,
-
await recordAnalytics(200);
-
expiration: new Date().toISOString(),
-
displayName: displayName,
-
pronouns: slackUser.profile.pronouns || null,
-
image: slackUser.profile.image_512,
-
await recordAnalytics(200);
-
expiration: user.expiration.toISOString(),
-
displayName: user.displayName,
-
pronouns: user.pronouns,
-
async function handleUserRedirect(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const parts = url.pathname.split("/");
-
const userId = parts[2] || "";
-
const user = await cache.getUser(userId);
-
// If not found then check slack first
-
if (!user || !user.imageUrl) {
-
let slackUser: SlackUser;
-
slackUser = await slackApp.getUserInfo(userId.toUpperCase());
-
if (e instanceof Error && e.message === "user_not_found") {
-
`\x1b[38;5;214m ⚠️ WARN\x1b[0m user not found: \x1b[38;5;208m${userId}\x1b[0m`,
-
await recordAnalytics(307);
-
return new Response(null, {
-
"https://api.dicebear.com/9.x/thumbs/svg?seed={username_hash}",
-
Sentry.withScope((scope) => {
-
scope.setExtra("url", request.url);
-
scope.setExtra("user", userId);
-
Sentry.captureException(e);
-
if (e instanceof Error)
-
`\x1b[38;5;214m ⚠️ WARN\x1b[0m error on fetching user from slack: \x1b[38;5;208m${e.message}\x1b[0m`,
-
await recordAnalytics(500);
-
{ message: `Error fetching user from Slack: ${e}` },
-
await cache.insertUser(
-
slackUser.profile.display_name_normalized ||
-
slackUser.profile.real_name_normalized,
-
slackUser.profile.pronouns,
-
slackUser.profile.image_512,
-
await recordAnalytics(302);
-
return new Response(null, {
-
headers: { Location: slackUser.profile.image_512 },
-
await recordAnalytics(302);
-
return new Response(null, {
-
headers: { Location: user.imageUrl },
-
async function handleListEmojis(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const emojis = await cache.listEmojis();
-
await recordAnalytics(200);
-
emojis.map((emoji) => ({
-
expiration: emoji.expiration.toISOString(),
-
...(emoji.alias ? { alias: emoji.alias } : {}),
-
async function handleGetEmoji(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const emojiName = url.pathname.split("/").pop() || "";
-
const emoji = await cache.getEmoji(emojiName);
-
const fallbackUrl = getEmojiUrl(emojiName);
-
await recordAnalytics(404);
-
return Response.json({ message: "Emoji not found" }, { status: 404 });
-
await recordAnalytics(200);
-
expiration: new Date().toISOString(),
-
await recordAnalytics(200);
-
expiration: emoji.expiration.toISOString(),
-
...(emoji.alias ? { alias: emoji.alias } : {}),
-
async function handleEmojiRedirect(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const parts = url.pathname.split("/");
-
const emojiName = parts[2] || "";
-
const emoji = await cache.getEmoji(emojiName);
-
const fallbackUrl = getEmojiUrl(emojiName);
-
await recordAnalytics(404);
-
return Response.json({ message: "Emoji not found" }, { status: 404 });
-
await recordAnalytics(302);
-
return new Response(null, {
-
headers: { Location: fallbackUrl },
-
await recordAnalytics(302);
-
return new Response(null, {
-
headers: { Location: emoji.imageUrl },
-
async function handleResetCache(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const authHeader = request.headers.get("authorization") || "";
-
if (authHeader !== `Bearer ${process.env.BEARER_TOKEN}`) {
-
await recordAnalytics(401);
-
return new Response("Unauthorized", { status: 401 });
-
const result = await cache.purgeAll();
-
await recordAnalytics(200);
-
return Response.json(result);
-
async function handlePurgeUser(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const authHeader = request.headers.get("authorization") || "";
-
if (authHeader !== `Bearer ${process.env.BEARER_TOKEN}`) {
-
await recordAnalytics(401);
-
return new Response("Unauthorized", { status: 401 });
-
const url = new URL(request.url);
-
const parts = url.pathname.split("/");
-
const userId = parts[2] || "";
-
const success = await cache.purgeUserCache(userId);
-
await recordAnalytics(200);
-
message: success ? "User cache purged" : "User not found in cache",
-
async function handleGetStats(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const params = new URLSearchParams(url.search);
-
const days = params.get("days") ? parseInt(params.get("days")!) : 7;
-
const analytics = await cache.getAnalytics(days);
-
await recordAnalytics(200);
-
return Response.json(analytics);
-
// Fast essential stats - just the 3 key metrics
-
async function handleGetEssentialStats(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const params = new URLSearchParams(url.search);
-
const days = params.get("days") ? parseInt(params.get("days")!) : 7;
-
const essentialStats = await cache.getEssentialStats(days);
-
await recordAnalytics(200);
-
return Response.json(essentialStats);
-
// Chart data - requests and latency over time
-
async function handleGetChartData(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const params = new URLSearchParams(url.search);
-
const days = params.get("days") ? parseInt(params.get("days")!) : 7;
-
const chartData = await cache.getChartData(days);
-
await recordAnalytics(200);
-
return Response.json(chartData);
-
// User agents data - slowest loading part
-
async function handleGetUserAgents(
-
recordAnalytics: (statusCode: number) => Promise<void>,
-
const url = new URL(request.url);
-
const params = new URLSearchParams(url.search);
-
const days = params.get("days") ? parseInt(params.get("days")!) : 7;
-
const userAgents = await cache.getUserAgents(days);
-
await recordAnalytics(200);
-
return Response.json(userAgents);
-
// Cache maintenance is now handled by scheduled tasks in cache.ts
-
// No aggressive daily purge needed - users will lazy load with longer TTL