From 0ff1c74c039ebf08ff8d91afb9a3f32e0a5d8d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sun, 24 Oct 2021 10:47:43 +0200 Subject: [PATCH] feat: add ctl for getting and putting KV settings This commit adds three commands to clankctl: * clankctl get-tag, to get the value of a tag. * clankctl put-tag, to set the value of a tag. * clankctl delete-tag, to delete the value of a tag. They are designed to be used through the command line in case something has to be debugged ever and the value of a specific key has to manually change, overriding any kind of abstraction built on top of the setting provider. Using this endpoint it is possible to get the specific value of a tag, no matter the preffix it is ussing. It is also possible to change manually the value of a tag, and to delete it. It avoids having to rely on manually touching the SQLite, given that the tags are stored as a big JSON blob anyway. --- src/clankctl.ts | 54 ++++++++++++++++++++++++++++ src/lib/http/client.ts | 25 ++++++++++++- src/lib/http/middlewares/guild.ts | 2 ++ src/lib/http/middlewares/provider.ts | 43 ++++++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/lib/http/middlewares/provider.ts diff --git a/src/clankctl.ts b/src/clankctl.ts index 06168028..1f78f972 100644 --- a/src/clankctl.ts +++ b/src/clankctl.ts @@ -87,6 +87,60 @@ clankctl.command( } ); +clankctl.command( + "get-tag ", + "get a preference from the setting provider", + () => ({}), + async (argv: { guild: string; key: string }) => { + client + .getProviderSetting(argv.guild, argv.key) + .then((value) => { + console.log(value); + process.exit(0); + }) + .catch((e) => { + console.error("Error: " + e); + process.exit(1); + }); + } +); + +clankctl.command( + "put-tag ", + "update or set a preference from the setting provider", + () => ({}), + async (argv: { guild: string; key: string; value: string }) => { + client + .setProviderSetting(argv.guild, argv.key, argv.value) + .then(() => { + console.log("OK"); + process.exit(0); + }) + .catch((e) => { + console.error("Error: " + e); + process.exit(1); + }); + } +); + +clankctl.command( + "delete-tag ", + "delete a preference from the setting provider", + () => ({}), + async (argv: { guild: string; key: string }) => { + client + .deleteProviderSetting(argv.guild, argv.key) + .then(() => { + console.log("OK"); + process.exit(0); + }) + .catch((e) => { + console.error("Error: " + e); + process.exit(1); + }); + } +); + clankctl.command( "set-config ", "update the configuration for a specific guild", diff --git a/src/lib/http/client.ts b/src/lib/http/client.ts index 31aaf99d..a874ba07 100644 --- a/src/lib/http/client.ts +++ b/src/lib/http/client.ts @@ -59,7 +59,7 @@ export default class Client { if (response.status !== 200) { throw new Error(`${response.statusText}: ${response.data}`); } - return (response.data as any).antiraid as boolean; + return (response.data as { antiraid: boolean }).antiraid; } async setRaidMode(mode: boolean): Promise { @@ -110,4 +110,27 @@ export default class Client { throw new Error(`${response.statusText}: ${response.data}`); } } + async getProviderSetting(guildId: string, tag: string): Promise { + const response = await this.client.get(`/guilds/${guildId}/provider/${tag}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`${response.statusText}: ${response.data}`); + } + } + + async setProviderSetting(guildId: string, tag: string, value: string): Promise { + const payload = { value }; + const response = await this.client.put(`/guilds/${guildId}/provider/${tag}`, payload); + if (response.status !== 204) { + throw new Error(`${response.statusText}: ${response.data}`); + } + } + + async deleteProviderSetting(guildId: string, tag: string): Promise { + const response = await this.client.delete(`/guilds/${guildId}/provider/${tag}`); + if (response.status !== 204) { + throw new Error(`${response.statusText}: ${response.data}`); + } + } } diff --git a/src/lib/http/middlewares/guild.ts b/src/lib/http/middlewares/guild.ts index bc9490e6..df123638 100644 --- a/src/lib/http/middlewares/guild.ts +++ b/src/lib/http/middlewares/guild.ts @@ -5,6 +5,7 @@ import Makibot from "../../../Makibot"; import Server from "../../server"; import autorolesMiddleware from "./autoroles"; import memberMiddleware from "./member"; +import providerMiddleware from "./provider"; import voiceRoleMiddleware from "./voiceroles"; export interface MiddlewareLocals { @@ -35,6 +36,7 @@ export default function guildMiddleware(makibot: Makibot): express.Router { router.use("/members/:member", memberMiddleware(makibot)); router.use("/roles/reactions", autorolesMiddleware(makibot)); router.use("/roles/voices", voiceRoleMiddleware(makibot)); + router.use("/provider/:tag", providerMiddleware(makibot)); /* Print current settings for the server. */ router.get("/settings", (req, res) => { diff --git a/src/lib/http/middlewares/provider.ts b/src/lib/http/middlewares/provider.ts new file mode 100644 index 00000000..a53ff3c7 --- /dev/null +++ b/src/lib/http/middlewares/provider.ts @@ -0,0 +1,43 @@ +import express from "express"; + +import Makibot from "../../../Makibot"; +import { MiddlewareLocals as GuildMiddlewareLocals } from "./guild"; + +type RouterRequest = express.Request< + { + tag: string; + }, + ResBody, + ReqBody, + unknown, + GuildMiddlewareLocals +>; + +export default function providerMiddleware(makibot: Makibot): express.Router { + const router = express.Router({ mergeParams: true }); + + router.get("/", (req: RouterRequest, res) => { + const value = makibot.provider.get(res.locals.guild.id, req.params.tag, undefined); + if (typeof value === "undefined") { + res.status(404).contentType("text/plain").send("tag not found"); + } else { + res.status(200).contentType("application/json").json(value); + } + }); + + router.put("/", async (req: RouterRequest<{ value: unknown }>, res) => { + if (req.body.value) { + await makibot.provider.set(res.locals.guild.id, req.params.tag, req.body.value); + return res.status(204).contentType("text/plain").send(); + } else { + return res.status(400).contentType("text/plain").send("invalid body"); + } + }); + + router.delete("/", async (req: RouterRequest, res) => { + await makibot.provider.remove(res.locals.guild.id, req.params.tag); + return res.status(204).contentType("text/plain").send(); + }); + + return router; +}