a cache for slack profile pictures and emojis
at main 3.4 kB view raw
1import * as Sentry from "@sentry/bun"; 2import { serve } from "bun"; 3import { getEmojiUrl } from "../utils/emojiHelper"; 4import { SlackCache } from "./cache"; 5import dashboard from "./dashboard.html"; 6import { buildRoutes, getSwaggerSpec } from "./lib/route-builder"; 7import { createApiRoutes } from "./routes/api-routes"; 8import { SlackWrapper } from "./slackWrapper"; 9import swagger from "./swagger.html"; 10 11// Initialize Sentry if DSN is provided 12if (process.env.SENTRY_DSN) { 13 console.log("Sentry DSN provided, error monitoring is enabled"); 14 Sentry.init({ 15 environment: process.env.NODE_ENV, 16 dsn: process.env.SENTRY_DSN, 17 tracesSampleRate: 0.5, 18 ignoreErrors: ["Not Found", "404", "user_not_found", "emoji_not_found"], 19 }); 20} else { 21 console.warn("Sentry DSN not provided, error monitoring is disabled"); 22} 23 24// Initialize SlackWrapper and Cache 25const slackApp = new SlackWrapper(); 26const cache = new SlackCache( 27 process.env.DATABASE_PATH ?? "./data/cachet.db", 28 25, 29 async () => { 30 console.log("Fetching emojis from Slack"); 31 const emojis = await slackApp.getEmojiList(); 32 const emojiEntries = Object.entries(emojis) 33 .map(([name, url]) => { 34 if (typeof url === "string" && url.startsWith("alias:")) { 35 const aliasName = url.substring(6); 36 const aliasUrl = emojis[aliasName] ?? getEmojiUrl(aliasName); 37 38 if (!aliasUrl) { 39 console.warn(`Could not find alias for ${aliasName}`); 40 return null; 41 } 42 43 return { 44 name, 45 imageUrl: aliasUrl, 46 alias: aliasName, 47 }; 48 } 49 return { 50 name, 51 imageUrl: url, 52 alias: null, 53 }; 54 }) 55 .filter( 56 ( 57 entry, 58 ): entry is { name: string; imageUrl: string; alias: string | null } => 59 entry !== null, 60 ); 61 62 console.log("Batch inserting emojis"); 63 await cache.batchInsertEmojis(emojiEntries); 64 console.log("Finished batch inserting emojis"); 65 }, 66); 67 68// Inject SlackWrapper into cache for background user updates 69cache.setSlackWrapper(slackApp); 70 71// Create the typed API routes with injected dependencies 72const apiRoutes = createApiRoutes(cache, slackApp); 73 74// Build Bun-compatible routes and generate Swagger 75const typedRoutes = buildRoutes(apiRoutes); 76const generatedSwagger = getSwaggerSpec(); 77 78// Legacy routes (non-API) 79const legacyRoutes = { 80 "/dashboard": dashboard, 81 "/swagger": swagger, 82 "/swagger.json": async (_: Request) => { 83 return Response.json(generatedSwagger); 84 }, 85 "/favicon.ico": async (_: Request) => { 86 return new Response(Bun.file("./favicon.ico")); 87 }, 88 89 // Root route - redirect to dashboard for browsers 90 "/": async (request: Request) => { 91 const userAgent = request.headers.get("user-agent") || ""; 92 93 if ( 94 userAgent.toLowerCase().includes("mozilla") || 95 userAgent.toLowerCase().includes("chrome") || 96 userAgent.toLowerCase().includes("safari") 97 ) { 98 return new Response(null, { 99 status: 302, 100 headers: { Location: "/dashboard" }, 101 }); 102 } 103 104 return new Response( 105 "Hello World from Cachet 😊\n\n---\nSee /swagger for docs\nSee /dashboard for analytics\n---", 106 ); 107 }, 108}; 109 110// Merge all routes 111const allRoutes = { 112 ...legacyRoutes, 113 ...typedRoutes, 114}; 115 116// Start the server 117const server = serve({ 118 routes: allRoutes, 119 port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000, 120}); 121 122console.log(`🚀 Server running on http://localhost:${server.port}`); 123 124export { cache, slackApp };