From fb98df8f984982ee1bff28475114e710a526854f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Tue, 3 Oct 2023 00:08:14 +0200 Subject: [PATCH] initialize redoc --- bun.lockb | Bin 96129 -> 96504 bytes package.json | 3 +- src/app.ts | 48 +++++++-- src/controllers/Client.controller.ts | 38 ++++--- src/core/Controller.ts | 4 +- src/models/Controller.ts | 4 + swagger.json | 154 +++++++++++++++++++++++++++ 7 files changed, 226 insertions(+), 25 deletions(-) create mode 100644 swagger.json diff --git a/bun.lockb b/bun.lockb index c94c7c985812708e7ad02a68e6ca36aee1373414..840c710910cd8ce30130eb06ceeae49a6c2d8b6c 100644 GIT binary patch delta 18228 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 IRQ0a=KZZ-+Z2$lO delta 17996 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 Ui*orOyFX90eCl+rIz9Zq0D1kW`2YX_ diff --git a/package.json b/package.json index 08e62eb..8a80fc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "legica-dana", - "version": "0.7.0", + "version": "0.7.5", "main": "src/app.ts", "scripts": { "start": "bun src/app.ts" @@ -17,6 +17,7 @@ "dotenv": "^8.2.0", "express": "^4.18.2", "express-basic-auth": "^1.2.1", + "redoc-express": "^2.1.0", "typescript": "^4.1.5" }, "devDependencies": { diff --git a/src/app.ts b/src/app.ts index 008d177..191ba59 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,9 +3,10 @@ import { Chat } from "@common"; import { Controller } from "@core"; import { ClientController } from "@controllers"; import express from "express"; -import { config } from "@constants"; -import basicAuth from "express-basic-auth"; +import { APP_VERSION, config } from "@constants"; import bodyParser from "body-parser"; +import redoc from "redoc-express"; +import path from "path"; const client: Client = new Client(); const chat: Chat = new Chat(client); @@ -13,16 +14,47 @@ const app = express(); app.use(bodyParser.json()); -app.use( - basicAuth({ - users: { - admin: config.PASSWORD, +app.get("/docs/swagger.json", (req, res) => { + res.sendFile("swagger.json", { root: path.join(__dirname, "..") }); +}); +app.get( + "/docs", + redoc({ + title: "API Docs", + specUrl: "/docs/swagger.json", + nonce: "", + redocOptions: { + theme: { + colors: { + primary: { + main: "#6EC5AB", + }, + }, + typography: { + fontFamily: `"museo-sans", 'Helvetica Neue', Helvetica, Arial, sans-serif`, + fontSize: "15px", + lineHeight: "1.5", + code: { + code: "#87E8C7", + backgroundColor: "#4D4D4E", + }, + }, + menu: { + backgroundColor: "#ffffff", + }, + }, }, }) ); -const controllers = new Controller([new ClientController(client, app)]); +app.get("version", (_, res) => { + res.send(APP_VERSION); +}); + +const controllers = new Controller(app, [new ClientController(client)]); controllers.register(); chat.register(config.TOKEN || ""); -app.listen(config.PORT); +app.listen(config.PORT, () => + console.log(`Legica bot API listening on port ${config.PORT}!`) +); diff --git a/src/controllers/Client.controller.ts b/src/controllers/Client.controller.ts index f6eb70d..06c8e35 100644 --- a/src/controllers/Client.controller.ts +++ b/src/controllers/Client.controller.ts @@ -2,13 +2,15 @@ import { Client, MessageEmbed, TextChannel } from "discord.js"; import * as cron from "cron"; import axios from "axios"; import cheerio from "cheerio"; -import { Express } from "express"; +import { Router } from "express"; import { IController, Legica } from "@models"; -import { APP_VERSION, config } from "@constants"; +import { config } from "@constants"; +import basicAuth from "express-basic-auth"; class ClientController implements IController { private legicaTask: cron.CronJob | null = null; - constructor(private client: Client, private app: Express) {} + public path: string = "task"; + constructor(private client: Client) {} public register = (): void => { this.client.on("ready", (): void => { @@ -20,24 +22,30 @@ class ClientController implements IController { "utc" ); }); + }; - this.app.get("", (_, res) => { + public registerRouter = (): Router => { + const router = Router(); + + router.use( + basicAuth({ + users: { + admin: config.PASSWORD, + }, + }) + ); + router.get("", (_, res) => { res.send(this.legicaTask?.running); }); - this.app.get("/next", (_, res) => { + router.get("next", (_, res) => { if (!this.legicaTask?.running) { res.status(400).send("Task is not running."); } else { res.send(this.legicaTask.nextDate().toISO()); } }); - - this.app.get("/version", (_, res) => { - res.send(APP_VERSION); - }); - - this.app.post("/start", (_, res) => { + router.post("", (_, res) => { if (this.legicaTask?.running) { res.status(400).send("Task already running."); } else { @@ -45,8 +53,7 @@ class ClientController implements IController { res.send("Task started."); } }); - - this.app.post("/stop", (_, res) => { + router.delete("", (_, res) => { if (!this.legicaTask?.running) { res.status(400).send("Task already stopped."); } else { @@ -55,7 +62,7 @@ class ClientController implements IController { } }); - this.app.post("/post-next", async (_, res) => { + router.post("send-latest", async (_, res) => { try { await this.sendNextMessage(); res.send(true); @@ -64,7 +71,7 @@ class ClientController implements IController { } }); - this.app.post("/post", async (req, res) => { + router.post("send", async (req, res) => { try { const url = req.body.url; await this.sendMessage(url); @@ -73,6 +80,7 @@ class ClientController implements IController { res.status(400).send(err); } }); + return router; }; private sendNextMessage = async (): Promise => { diff --git a/src/core/Controller.ts b/src/core/Controller.ts index 6a7e088..b3a7089 100644 --- a/src/core/Controller.ts +++ b/src/core/Controller.ts @@ -1,11 +1,13 @@ import { IController } from "models"; +import { Express } from "express"; class Controller { - constructor(private controllers: IController[]) {} + constructor(private app: Express, private controllers: IController[]) {} public register = (): void => { this.controllers?.forEach((controller) => { controller.register(); + this.app.use(controller.path || "", controller.registerRouter()); }); }; } diff --git a/src/models/Controller.ts b/src/models/Controller.ts index b39f569..3816a37 100644 --- a/src/models/Controller.ts +++ b/src/models/Controller.ts @@ -1,3 +1,7 @@ +import { Router } from "express"; + export interface IController { register(): void; + registerRouter(): Router; + path: string; } diff --git a/swagger.json b/swagger.json new file mode 100644 index 0000000..e28a439 --- /dev/null +++ b/swagger.json @@ -0,0 +1,154 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Legica Bot API", + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "0.7.0" + }, + "tags": [ + { + "name": "api", + "description": "API information" + }, + { + "name": "task", + "description": "Everything about the task" + } + ], + "paths": { + "/version": { + "get": { + "tags": [ + "api" + ], + "summary": "Display current API version.", + "description": "Displays the current API version defined in package.json.", + "responses": { + "200": { + "description": "Successful operation" + } + } + } + }, + "/task": { + "get": { + "tags": [ + "task" + ], + "summary": "Check if task is running.", + "description": "Retrieve the current state of scheduled task.", + "responses": { + "200": { + "description": "Successful operation" + } + } + }, + "post": { + "tags": [ + "task" + ], + "summary": "Start task if it is not running.", + "description": "Starts the task if it is not currently running.", + "responses": { + "200": { + "description": "Task started." + }, + "400": { + "description": "Task already running." + } + } + }, + "delete": { + "tags": [ + "task" + ], + "summary": "Stop task if it is running.", + "description": "Stops the task if it is currently running.", + "responses": { + "200": { + "description": "Task stopped." + }, + "400": { + "description": "Task already stopped." + } + } + } + }, + "/task/next": { + "get": { + "tags": [ + "task" + ], + "summary": "Check when the task is scheduled due next.", + "description": "Retrieve the datetime when task is scheduled to execute.", + "responses": { + "200": { + "description": "Next datetime" + }, + "400": { + "description": "Task is not running." + } + } + } + }, + "/task/send-latest": { + "post": { + "tags": [ + "task" + ], + "summary": "Send latest post of legica dana.", + "description": "Sends latest post of legica dana to all discord channels.", + "responses": { + "200": { + "description": "Confirmation." + } + } + } + }, + "/task/send": { + "post": { + "tags": [ + "task" + ], + "summary": "Send post of legica dana.", + "description": "Sends provided post of legica dana to all discord channels.", + "requestBody": { + "description": "URL", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Legica" + } + } + } + }, + "responses": { + "200": { + "description": "Confirmation." + } + } + } + } + }, + "components": { + "schemas": { + "Legica": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "string", + "example": "https://sib.net.hr/legica-dana/4390659/legica-dana-2992023/" + } + }, + "xml": { + "name": "order" + } + } + } + } + } \ No newline at end of file