initialize redoc

This commit is contained in:
Fran Jurmanović
2023-10-03 00:08:14 +02:00
parent 224c2f32c7
commit fb98df8f98
7 changed files with 226 additions and 25 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{ {
"name": "legica-dana", "name": "legica-dana",
"version": "0.7.0", "version": "0.7.5",
"main": "src/app.ts", "main": "src/app.ts",
"scripts": { "scripts": {
"start": "bun src/app.ts" "start": "bun src/app.ts"
@@ -17,6 +17,7 @@
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-basic-auth": "^1.2.1", "express-basic-auth": "^1.2.1",
"redoc-express": "^2.1.0",
"typescript": "^4.1.5" "typescript": "^4.1.5"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -3,9 +3,10 @@ import { Chat } from "@common";
import { Controller } from "@core"; import { Controller } from "@core";
import { ClientController } from "@controllers"; import { ClientController } from "@controllers";
import express from "express"; import express from "express";
import { config } from "@constants"; import { APP_VERSION, config } from "@constants";
import basicAuth from "express-basic-auth";
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import redoc from "redoc-express";
import path from "path";
const client: Client = new Client(); const client: Client = new Client();
const chat: Chat = new Chat(client); const chat: Chat = new Chat(client);
@@ -13,16 +14,47 @@ const app = express();
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use( app.get("/docs/swagger.json", (req, res) => {
basicAuth({ res.sendFile("swagger.json", { root: path.join(__dirname, "..") });
users: { });
admin: config.PASSWORD, 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(); controllers.register();
chat.register(config.TOKEN || ""); chat.register(config.TOKEN || "");
app.listen(config.PORT); app.listen(config.PORT, () =>
console.log(`Legica bot API listening on port ${config.PORT}!`)
);

View File

@@ -2,13 +2,15 @@ import { Client, MessageEmbed, TextChannel } from "discord.js";
import * as cron from "cron"; import * as cron from "cron";
import axios from "axios"; import axios from "axios";
import cheerio from "cheerio"; import cheerio from "cheerio";
import { Express } from "express"; import { Router } from "express";
import { IController, Legica } from "@models"; 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 { class ClientController implements IController {
private legicaTask: cron.CronJob | null = null; private legicaTask: cron.CronJob | null = null;
constructor(private client: Client, private app: Express) {} public path: string = "task";
constructor(private client: Client) {}
public register = (): void => { public register = (): void => {
this.client.on("ready", (): void => { this.client.on("ready", (): void => {
@@ -20,24 +22,30 @@ class ClientController implements IController {
"utc" "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); res.send(this.legicaTask?.running);
}); });
this.app.get("/next", (_, res) => { router.get("next", (_, res) => {
if (!this.legicaTask?.running) { if (!this.legicaTask?.running) {
res.status(400).send("Task is not running."); res.status(400).send("Task is not running.");
} else { } else {
res.send(this.legicaTask.nextDate().toISO()); res.send(this.legicaTask.nextDate().toISO());
} }
}); });
router.post("", (_, res) => {
this.app.get("/version", (_, res) => {
res.send(APP_VERSION);
});
this.app.post("/start", (_, res) => {
if (this.legicaTask?.running) { if (this.legicaTask?.running) {
res.status(400).send("Task already running."); res.status(400).send("Task already running.");
} else { } else {
@@ -45,8 +53,7 @@ class ClientController implements IController {
res.send("Task started."); res.send("Task started.");
} }
}); });
router.delete("", (_, res) => {
this.app.post("/stop", (_, res) => {
if (!this.legicaTask?.running) { if (!this.legicaTask?.running) {
res.status(400).send("Task already stopped."); res.status(400).send("Task already stopped.");
} else { } else {
@@ -55,7 +62,7 @@ class ClientController implements IController {
} }
}); });
this.app.post("/post-next", async (_, res) => { router.post("send-latest", async (_, res) => {
try { try {
await this.sendNextMessage(); await this.sendNextMessage();
res.send(true); 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 { try {
const url = req.body.url; const url = req.body.url;
await this.sendMessage(url); await this.sendMessage(url);
@@ -73,6 +80,7 @@ class ClientController implements IController {
res.status(400).send(err); res.status(400).send(err);
} }
}); });
return router;
}; };
private sendNextMessage = async (): Promise<void> => { private sendNextMessage = async (): Promise<void> => {

View File

@@ -1,11 +1,13 @@
import { IController } from "models"; import { IController } from "models";
import { Express } from "express";
class Controller { class Controller {
constructor(private controllers: IController[]) {} constructor(private app: Express, private controllers: IController[]) {}
public register = (): void => { public register = (): void => {
this.controllers?.forEach((controller) => { this.controllers?.forEach((controller) => {
controller.register(); controller.register();
this.app.use(controller.path || "", controller.registerRouter());
}); });
}; };
} }

View File

@@ -1,3 +1,7 @@
import { Router } from "express";
export interface IController { export interface IController {
register(): void; register(): void;
registerRouter(): Router;
path: string;
} }

154
swagger.json Normal file
View File

@@ -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"
}
}
}
}
}