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;