a cache for slack profile pictures and emojis
at v0.3.2 16 kB view raw
1import { serve } from "bun"; 2import { version } from "../package.json"; 3 4// Define the Swagger specification 5const swaggerSpec = { 6 openapi: "3.0.0", 7 info: { 8 title: "Cachet", 9 version: version, 10 description: 11 "Hi 👋\n\nThis is a pretty simple API that acts as a middleman caching layer between slack and the outside world. There may be authentication in the future, but for now, it's just a simple cache.\n\nThe `/r` endpoints are redirects to the actual image URLs, so you can use them as direct image links.", 12 contact: { 13 name: "Kieran Klukas", 14 email: "me@dunkirk.sh", 15 }, 16 license: { 17 name: "AGPL 3.0", 18 url: "https://github.com/taciturnaxoltol/cachet/blob/master/LICENSE.md", 19 }, 20 }, 21 tags: [ 22 { 23 name: "The Cache!", 24 description: "*must be read in an ominous voice*", 25 }, 26 { 27 name: "Status", 28 description: "*Rather boring status endpoints :(*", 29 }, 30 ], 31 paths: { 32 "/users/{user}": { 33 get: { 34 tags: ["The Cache!"], 35 summary: "Get user information", 36 description: 37 "Retrieves user information from the cache or from Slack if not cached", 38 parameters: [ 39 { 40 name: "user", 41 in: "path", 42 required: true, 43 schema: { 44 type: "string", 45 }, 46 description: "Slack user ID", 47 }, 48 ], 49 responses: { 50 "200": { 51 description: "User information", 52 content: { 53 "application/json": { 54 schema: { 55 type: "object", 56 properties: { 57 id: { 58 type: "string", 59 example: "90750e24-c2f0-4c52-8681-e6176da6e7ab", 60 }, 61 expiration: { 62 type: "string", 63 format: "date-time", 64 example: new Date().toISOString(), 65 }, 66 user: { 67 type: "string", 68 example: "U12345678", 69 }, 70 displayName: { 71 type: "string", 72 example: "krn", 73 }, 74 pronouns: { 75 type: "string", 76 nullable: true, 77 example: "possibly/blank", 78 }, 79 image: { 80 type: "string", 81 example: 82 "https://avatars.slack-edge.com/2024-11-30/8105375749571_53898493372773a01a1f_original.jpg", 83 }, 84 }, 85 }, 86 }, 87 }, 88 }, 89 "404": { 90 description: "User not found", 91 content: { 92 "application/json": { 93 schema: { 94 type: "object", 95 properties: { 96 message: { 97 type: "string", 98 example: "User not found", 99 }, 100 }, 101 }, 102 }, 103 }, 104 }, 105 "500": { 106 description: "Error fetching user from Slack", 107 content: { 108 "application/json": { 109 schema: { 110 type: "object", 111 properties: { 112 message: { 113 type: "string", 114 example: "Error fetching user from Slack", 115 }, 116 }, 117 }, 118 }, 119 }, 120 }, 121 }, 122 }, 123 }, 124 "/users/{user}/r": { 125 get: { 126 tags: ["The Cache!"], 127 summary: "Redirect to user profile image", 128 description: "Redirects to the user's profile image URL", 129 parameters: [ 130 { 131 name: "user", 132 in: "path", 133 required: true, 134 schema: { 135 type: "string", 136 }, 137 description: "Slack user ID", 138 }, 139 ], 140 responses: { 141 "302": { 142 description: "Redirect to user profile image", 143 }, 144 "307": { 145 description: "Redirect to default image when user not found", 146 }, 147 "500": { 148 description: "Error fetching user from Slack", 149 content: { 150 "application/json": { 151 schema: { 152 type: "object", 153 properties: { 154 message: { 155 type: "string", 156 example: "Error fetching user from Slack", 157 }, 158 }, 159 }, 160 }, 161 }, 162 }, 163 }, 164 }, 165 }, 166 "/users/{user}/purge": { 167 post: { 168 tags: ["The Cache!"], 169 summary: "Purge user cache", 170 description: "Purges a specific user's cache", 171 parameters: [ 172 { 173 name: "user", 174 in: "path", 175 required: true, 176 schema: { 177 type: "string", 178 }, 179 description: "Slack user ID", 180 }, 181 { 182 name: "authorization", 183 in: "header", 184 required: true, 185 schema: { 186 type: "string", 187 example: "Bearer <token>", 188 }, 189 description: "Bearer token for authentication", 190 }, 191 ], 192 responses: { 193 "200": { 194 description: "User cache purged", 195 content: { 196 "application/json": { 197 schema: { 198 type: "object", 199 properties: { 200 message: { 201 type: "string", 202 example: "User cache purged", 203 }, 204 userId: { 205 type: "string", 206 example: "U12345678", 207 }, 208 success: { 209 type: "boolean", 210 example: true, 211 }, 212 }, 213 }, 214 }, 215 }, 216 }, 217 "401": { 218 description: "Unauthorized", 219 content: { 220 "text/plain": { 221 schema: { 222 type: "string", 223 example: "Unauthorized", 224 }, 225 }, 226 }, 227 }, 228 }, 229 }, 230 }, 231 "/emojis": { 232 get: { 233 tags: ["The Cache!"], 234 summary: "Get all emojis", 235 description: "Retrieves all emojis from the cache", 236 responses: { 237 "200": { 238 description: "List of emojis", 239 content: { 240 "application/json": { 241 schema: { 242 type: "array", 243 items: { 244 type: "object", 245 properties: { 246 id: { 247 type: "string", 248 example: "5427fe70-686f-4684-9da5-95d9ef4c1090", 249 }, 250 expiration: { 251 type: "string", 252 format: "date-time", 253 example: new Date().toISOString(), 254 }, 255 name: { 256 type: "string", 257 example: "blahaj-heart", 258 }, 259 alias: { 260 type: "string", 261 nullable: true, 262 example: "blobhaj-heart", 263 }, 264 image: { 265 type: "string", 266 example: 267 "https://emoji.slack-edge.com/T0266FRGM/blahaj-heart/db9adf8229e9a4fb.png", 268 }, 269 }, 270 }, 271 }, 272 }, 273 }, 274 }, 275 }, 276 }, 277 }, 278 "/emojis/{emoji}": { 279 get: { 280 tags: ["The Cache!"], 281 summary: "Get emoji information", 282 description: "Retrieves information about a specific emoji", 283 parameters: [ 284 { 285 name: "emoji", 286 in: "path", 287 required: true, 288 schema: { 289 type: "string", 290 }, 291 description: "Emoji name", 292 }, 293 ], 294 responses: { 295 "200": { 296 description: "Emoji information", 297 content: { 298 "application/json": { 299 schema: { 300 type: "object", 301 properties: { 302 id: { 303 type: "string", 304 example: "9ed0a560-928d-409c-89fc-10fe156299da", 305 }, 306 expiration: { 307 type: "string", 308 format: "date-time", 309 example: new Date().toISOString(), 310 }, 311 name: { 312 type: "string", 313 example: "orphmoji-yay", 314 }, 315 image: { 316 type: "string", 317 example: 318 "https://emoji.slack-edge.com/T0266FRGM/orphmoji-yay/23a37f4af47092d3.png", 319 }, 320 }, 321 }, 322 }, 323 }, 324 }, 325 "404": { 326 description: "Emoji not found", 327 content: { 328 "application/json": { 329 schema: { 330 type: "object", 331 properties: { 332 message: { 333 type: "string", 334 example: "Emoji not found", 335 }, 336 }, 337 }, 338 }, 339 }, 340 }, 341 }, 342 }, 343 }, 344 "/emojis/{emoji}/r": { 345 get: { 346 tags: ["The Cache!"], 347 summary: "Redirect to emoji image", 348 description: "Redirects to the emoji image URL", 349 parameters: [ 350 { 351 name: "emoji", 352 in: "path", 353 required: true, 354 schema: { 355 type: "string", 356 }, 357 description: "Emoji name", 358 }, 359 ], 360 responses: { 361 "302": { 362 description: "Redirect to emoji image", 363 }, 364 "404": { 365 description: "Emoji not found", 366 content: { 367 "application/json": { 368 schema: { 369 type: "object", 370 properties: { 371 message: { 372 type: "string", 373 example: "Emoji not found", 374 }, 375 }, 376 }, 377 }, 378 }, 379 }, 380 }, 381 }, 382 }, 383 "/reset": { 384 post: { 385 tags: ["The Cache!"], 386 summary: "Reset cache", 387 description: "Purges all items from the cache", 388 parameters: [ 389 { 390 name: "authorization", 391 in: "header", 392 required: true, 393 schema: { 394 type: "string", 395 example: "Bearer <token>", 396 }, 397 description: "Bearer token for authentication", 398 }, 399 ], 400 responses: { 401 "200": { 402 description: "Cache purged", 403 content: { 404 "application/json": { 405 schema: { 406 type: "object", 407 properties: { 408 message: { 409 type: "string", 410 example: "Cache purged", 411 }, 412 users: { 413 type: "number", 414 example: 10, 415 }, 416 emojis: { 417 type: "number", 418 example: 100, 419 }, 420 }, 421 }, 422 }, 423 }, 424 }, 425 "401": { 426 description: "Unauthorized", 427 content: { 428 "text/plain": { 429 schema: { 430 type: "string", 431 example: "Unauthorized", 432 }, 433 }, 434 }, 435 }, 436 }, 437 }, 438 }, 439 "/health": { 440 get: { 441 tags: ["Status"], 442 summary: "Health check", 443 description: 444 "Checks the health of the API, Slack connection, and database", 445 responses: { 446 "200": { 447 description: "Health check passed", 448 content: { 449 "application/json": { 450 schema: { 451 type: "object", 452 properties: { 453 http: { 454 type: "boolean", 455 example: true, 456 }, 457 slack: { 458 type: "boolean", 459 example: true, 460 }, 461 database: { 462 type: "boolean", 463 example: true, 464 }, 465 }, 466 }, 467 }, 468 }, 469 }, 470 "500": { 471 description: "Health check failed", 472 content: { 473 "application/json": { 474 schema: { 475 type: "object", 476 properties: { 477 http: { 478 type: "boolean", 479 example: false, 480 }, 481 slack: { 482 type: "boolean", 483 example: false, 484 }, 485 database: { 486 type: "boolean", 487 example: false, 488 }, 489 }, 490 }, 491 }, 492 }, 493 }, 494 }, 495 }, 496 }, 497 "/stats": { 498 get: { 499 tags: ["Status"], 500 summary: "Get analytics statistics", 501 description: "Retrieves analytics statistics for the API", 502 parameters: [ 503 { 504 name: "days", 505 in: "query", 506 required: false, 507 schema: { 508 type: "string", 509 }, 510 description: "Number of days to look back (default: 7)", 511 }, 512 ], 513 responses: { 514 "200": { 515 description: "Analytics statistics", 516 content: { 517 "application/json": { 518 schema: { 519 type: "object", 520 properties: { 521 totalRequests: { 522 type: "number", 523 }, 524 requestsByEndpoint: { 525 type: "array", 526 items: { 527 type: "object", 528 properties: { 529 endpoint: { 530 type: "string", 531 }, 532 count: { 533 type: "number", 534 }, 535 averageResponseTime: { 536 type: "number", 537 }, 538 }, 539 }, 540 }, 541 // Additional properties omitted for brevity 542 }, 543 }, 544 }, 545 }, 546 }, 547 }, 548 }, 549 }, 550 }, 551}; 552 553// Export the Swagger specification for use in other files 554export default swaggerSpec;