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