From 89507f8412116c4828f9cc5186c975b6771e686f Mon Sep 17 00:00:00 2001 From: aronmal Date: Sun, 10 Mar 2024 17:12:50 +0100 Subject: [PATCH] Fix: Using bigint and added backend testing --- .gitignore | 1 + discord_client_testing.http | 30 -- e2e/auth.spec.ts | 166 +++++++- package.json | 1 + pnpm-lock.yaml | 13 + public/api/specs/liljudd.json | 389 ++++++++++++++---- src/drizzle/schema.ts | 30 +- src/lib/responseBuilders.ts | 41 +- src/lib/responses.ts | 1 + src/lib/zod.ts | 12 +- src/middleware.ts | 64 ++- src/routes/api/[guildId]/config.ts | 36 +- src/routes/api/[guildId]/matches.ts | 17 +- .../{tp_messages.ts => timePlanning.ts} | 49 ++- src/routes/api/boot.ts | 6 +- src/types/db.d.ts | 4 +- src/types/env.d.ts | 1 - src/types/liljudd.d.ts | 244 +++++++---- src/types/lucia-auth.d.ts | 5 +- src/types/vinxi.d.ts | 12 + 20 files changed, 830 insertions(+), 292 deletions(-) delete mode 100644 discord_client_testing.http rename src/routes/api/[guildId]/{tp_messages.ts => timePlanning.ts} (67%) create mode 100644 src/types/vinxi.d.ts diff --git a/.gitignore b/.gitignore index 397e161..89ddf1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ src/drizzle/migrations +log dist .vinxi diff --git a/discord_client_testing.http b/discord_client_testing.http deleted file mode 100644 index 0d30605..0000000 --- a/discord_client_testing.http +++ /dev/null @@ -1,30 +0,0 @@ -GET https://discord.com/api/users/@me -Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}} - -### - -GET https://discord.com/api/users/@me -Authorization: Bot {{$dotenv DISCORD_BOT_TOKEN}} - -### - -GET https://discord.com/api/users/@me/guilds -Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}} - -### - -GET https://discord.com/api/users/@me/guilds/{{$dotenv DISCORD_GUILD_ID}}/member -Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}} - -### - -GET https://discord.com/api/guilds/{{$dotenv DISCORD_GUILD_ID}} -Authorization: Bot {{$dotenv DISCORD_BOT_TOKEN}} - -### - -POST https://discord.com/api/oauth2/token/revoke -Content-Type: application/x-www-form-urlencoded -Authorization: Basic {{$dotenv DISCORD_CLIENT_ID}}:{{$dotenv DISCORD_CLIENT_SECRET}} - -token={{$dotenv DISCORD_ACCESS_TOKEN}}&token_type_hint=access_token diff --git a/e2e/auth.spec.ts b/e2e/auth.spec.ts index 8938745..f713feb 100644 --- a/e2e/auth.spec.ts +++ b/e2e/auth.spec.ts @@ -1,14 +1,18 @@ +import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; import { createId } from "@paralleldrive/cuid2"; import { expect, test, type BrowserContext, type Page } from "@playwright/test"; +import "dotenv/config"; +import { eq } from "drizzle-orm"; +import { drizzle } from "drizzle-orm/postgres-js"; import { Lucia, type Cookie } from "lucia"; import createClient from "openapi-fetch"; -import * as schema from "~/drizzle/schema"; -import { paths } from "~/types/discord"; - -import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; -import "dotenv/config"; -import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; +import * as schema from "~/drizzle/schema"; +import type * as discord from "~/types/discord"; +import type * as liljudd from "~/types/liljudd"; + +const unencoded = `${process.env.DISCORD_CLIENT_ID}:${process.env.DISCORD_CLIENT_SECRET}`; +const encoded = btoa(unencoded); const queryClient = postgres(process.env.DATABASE_URL!); const db = drizzle(queryClient, { @@ -25,7 +29,22 @@ let page: Page; let sessionCookie: Cookie | undefined; +let userId = createId(); +let guildId: bigint; + test.describe.serial("User auth process", () => { + test.beforeAll(() => { + expect( + [ + "DISCORD_CLIENT_ID", + "DISCORD_CLIENT_SECRET", + "DATABASE_URL", + "DISCORD_BOT_TOKEN", + ].filter((e) => typeof process.env[e] === "undefined").length, + { message: "Please specify all env vars." }, + ).toBeFalsy(); + }); + test.beforeAll(async ({ browser }) => { context = await browser.newContext(); page = await context.newPage(); @@ -59,6 +78,14 @@ test.describe.serial("User auth process", () => { ]); }); + test.afterAll("Delete DB entries", async () => { + await db.delete(schema.users).where(eq(schema.users.id, userId)).execute(); + await db + .delete(schema.guilds) + .where(eq(schema.guilds.id, guildId)) + .execute(); + }); + test.afterAll(async () => { await context.close(); }); @@ -75,8 +102,8 @@ test.describe.serial("User auth process", () => { await page.waitForURL("/"); }); - test("Generate auth session for further tests", async () => { - const { GET } = createClient({ + test("Generate auth session for further tests", async ({ browser }) => { + const { GET } = createClient({ baseUrl: "https://discord.com/api/v10", }); const discordUserResponse = await GET("/users/@me", { @@ -86,7 +113,22 @@ test.describe.serial("User auth process", () => { }); if (discordUserResponse.error) throw discordUserResponse.error; const discordUser = discordUserResponse.data; - const userId = createId(); + + const browserName = browser.browserType().name() as + | "chromium" + | "webkit" + | "firefox"; + + userId = discordUser.id + userId.slice(discordUser.id.length); + userId = userId.slice(0, -browserName.length) + browserName; + + enum BrowserIds { + chromium, + webkit, + firefox, + } + guildId = BigInt(discordUser.id) ^ BigInt(BrowserIds[browserName]); + await db.insert(schema.users).values({ id: userId, discord_id: discordUser.id, @@ -119,4 +161,110 @@ test.describe.serial("User auth process", () => { "landing_page_logged_in.png", ); }); + + test("Test Api", async () => { + const { GET, POST, PUT } = createClient({ + baseUrl: "http://localhost:3000/", + }); + + const createConfigResponse = await POST("/api/{guildId}/config", { + params: { + path: { + guildId: guildId.toString(), + }, + }, + headers: { + Authorization: `Basic ${encoded}`, + Origin: "http://localhost:3000", + }, + }); + + if (createConfigResponse.error) + throw new Error(createConfigResponse.error.error); + + let getConfigResponse = await GET("/api/{guildId}/config", { + params: { + path: { + guildId: guildId.toString(), + }, + }, + headers: { + Authorization: `Basic ${encoded}`, + Origin: "http://localhost:3000", + }, + }); + + if (getConfigResponse.error) throw new Error(getConfigResponse.error.error); + + switch (getConfigResponse.data?.checksum) { + case "209a644c31a5ef123c432c2885d231a2e0efc4de": // chromium + case "aead21e132a94ab897ec28e0f0c337a66207bad3": // webkit + case "c3e2ff2ce5a8936234552125a54c2fe1ce1a35da": // firefox + break; + + default: + throw new Error( + "Before guild GET checksum didn't matched known ones: " + + getConfigResponse.data?.checksum, + ); + } + + const putTimePlanningResponse = await PUT("/api/{guildId}/timePlanning", { + body: { + enabled: true, + channelId: "1234567890123456789", + rolesEnabled: true, + isAvailableRoleId: "1234567890123456789", + wantsToBeNotifieRoledId: "1234567890123456789", + messageIds: { + "0": "1234567890123456789", + "1": "1234567890123456789", + "2": "1234567890123456789", + "3": "1234567890123456789", + "4": "1234567890123456789", + "5": "1234567890123456789", + "6": "1234567890123456789", + }, + }, + params: { + path: { + guildId: guildId.toString(), + }, + }, + headers: { + Authorization: `Basic ${encoded}`, + Origin: "http://localhost:3000", + }, + }); + + if (putTimePlanningResponse.error) + throw new Error(putTimePlanningResponse.error.error); + + getConfigResponse = await GET("/api/{guildId}/config", { + params: { + path: { + guildId: guildId.toString(), + }, + }, + headers: { + Authorization: `Basic ${encoded}`, + Origin: "http://localhost:3000", + }, + }); + + if (getConfigResponse.error) throw new Error(getConfigResponse.error.error); + + switch (getConfigResponse.data?.checksum) { + case "681c8324b21096255d942bb78bd6655da90d352e": // chromium + case "a2fb3601b7d0949b1ceada3b3ac0ba408c6159bb": // webkit + case "bf20daba95e8f3ddd17cc64e8a7ba184b68ad37b": // firefox + break; + + default: + throw new Error( + "After guild GET checksum didn't matched known ones: " + + getConfigResponse.data?.checksum, + ); + } + }); }); diff --git a/package.json b/package.json index 536f8f5..9ff0521 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@solidjs/router": "^0.12.4", "@solidjs/start": "^0.6.0", "arctic": "^1.2.1", + "colors": "^1.4.0", "drizzle-orm": "^0.29.4", "http-status": "^1.7.4", "json-stable-stringify": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2b1388..9f175cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ dependencies: arctic: specifier: ^1.2.1 version: 1.2.1 + colors: + specifier: ^1.4.0 + version: 1.4.0 drizzle-orm: specifier: ^0.29.4 version: 0.29.4(pg@8.11.3)(postgres@3.4.3) @@ -2761,6 +2764,11 @@ packages: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} dev: false + /colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + dev: false + /commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: false @@ -3370,6 +3378,7 @@ packages: /eslint-config-prettier@9.1.0(eslint@8.57.0): resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: @@ -4671,6 +4680,7 @@ packages: /nitropack@2.8.1: resolution: {integrity: sha512-pODv2kEEzZSDQR+1UMXbGyNgMedUDq/qUomtiAnQKQvLy52VGlecXO1xDfH3i0kP1yKEcKTnWsx1TAF5gHM7xQ==} engines: {node: ^16.11.0 || >=17.0.0} + hasBin: true peerDependencies: xml2js: ^0.6.2 peerDependenciesMeta: @@ -5344,6 +5354,7 @@ packages: /rollup-plugin-visualizer@5.12.0(rollup@4.12.0): resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} engines: {node: '>=14'} + hasBin: true peerDependencies: rollup: 2.x || 3.x || 4.x peerDependenciesMeta: @@ -6048,6 +6059,7 @@ packages: /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: @@ -6265,6 +6277,7 @@ packages: /vite@5.1.1(@types/node@20.11.22)(sass@1.71.1): resolution: {integrity: sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==} engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: '@types/node': ^18.0.0 || >=20.0.0 less: '*' diff --git a/public/api/specs/liljudd.json b/public/api/specs/liljudd.json index 2aaee56..8a6e2de 100644 --- a/public/api/specs/liljudd.json +++ b/public/api/specs/liljudd.json @@ -31,13 +31,34 @@ } }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -77,13 +98,92 @@ } }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "security": [ + { + "basicAuth": [] + } + ] + }, + "post": { + "tags": ["Guild config"], + "summary": "Creates a guild's config by ID", + "description": "Create a guild's config when the bot is has joined a new guild.", + "operationId": "postGuildById", + "parameters": [ + { + "name": "guildId", + "in": "path", + "description": "ID of guild's config to create", + "required": true, + "schema": { + "type": "string", + "format": "varchar(20)" + } + } + ], + "responses": { + "204": { + "description": "successful operation" + }, + "400": { + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -101,7 +201,7 @@ { "name": "guildId", "in": "path", - "description": "ID of guild config to delete", + "description": "ID of guild's config to delete", "required": true, "schema": { "type": "string", @@ -114,13 +214,34 @@ "description": "successful operation" }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -130,17 +251,17 @@ ] } }, - "/api/{guildId}/tp_messages": { + "/api/{guildId}/timePlanning": { "get": { "tags": ["Time planning messages"], - "summary": "Find the tp_messages of guild by ID", - "description": "Returns tp_messages for a guild", - "operationId": "getTp_messagesOfGuildById", + "summary": "Find the timePlanning of guild by ID", + "description": "Returns timePlanning for a guild", + "operationId": "gettimePlanningOfGuildById", "parameters": [ { "name": "guildId", "in": "path", - "description": "ID of guild's tp_messages to return", + "description": "ID of guild's timePlanning to return", "required": true, "schema": { "type": "string", @@ -154,22 +275,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/tp_messages" + "$ref": "#/components/schemas/timePlanning" } } } }, - "204": { - "description": "Time planning not enabled for this guild" - }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -180,14 +319,14 @@ }, "put": { "tags": ["Time planning messages"], - "summary": "Put new message IDs for tp_messages of guild by ID", - "description": "Returns tp_messages for a guild", - "operationId": "putTp_messagesOfGuildById", + "summary": "Put new message IDs for timePlanning of guild by ID", + "description": "Returns timePlanning for a guild", + "operationId": "puttimePlanningOfGuildById", "parameters": [ { "name": "guildId", "in": "path", - "description": "ID of guild's tp_messages to return", + "description": "ID of guild's timePlanning to return", "required": true, "schema": { "type": "string", @@ -196,11 +335,11 @@ } ], "requestBody": { - "description": "Put new message IDs for tp_messages in channel", + "description": "Put new message IDs for timePlanning in channel", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/tp_messages" + "$ref": "#/components/schemas/timePlanning" } } }, @@ -211,16 +350,34 @@ "description": "successful operation" }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" - }, - "403": { - "description": "Time planning not enabled for this guild" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -234,13 +391,13 @@ "get": { "tags": ["Matches"], "summary": "Find all matches of guild by ID", - "description": "Returns tp_messages for a guild", + "description": "Returns timePlanning for a guild", "operationId": "getMatchesOfGuildById", "parameters": [ { "name": "guildId", "in": "path", - "description": "ID of guild's tp_messages to return", + "description": "ID of guild's timePlanning to return", "required": true, "schema": { "type": "string", @@ -273,17 +430,35 @@ } } }, - "204": { - "description": "Time planning not enabled for this guild" - }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -295,7 +470,7 @@ "post": { "tags": ["Matches"], "summary": "Save a new created match of guild by ID", - "description": "Returns tp_messages for a guild", + "description": "Returns timePlanning for a guild", "operationId": "postMatchOfGuildById", "parameters": [ { @@ -337,13 +512,34 @@ "description": "successful operation" }, "400": { - "description": "Invalid ID supplied" + "description": "Invalid ID supplied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "401": { - "description": "Unauthorized" + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } }, "404": { - "description": "Guild not found" + "description": "Guild not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } } }, "security": [ @@ -361,8 +557,7 @@ "required": ["guildId", "timezone", "features", "matches", "checksum"], "properties": { "guildId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/id" }, "timezone": { "type": "string", @@ -388,9 +583,7 @@ "type": "boolean" }, "channelId": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "targetMinute": { "type": "number", @@ -416,14 +609,10 @@ "type": "boolean" }, "isAvailableRoleId": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "wantsToBeNotifieRoledId": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" } } } @@ -455,20 +644,16 @@ ], "properties": { "channelId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/id" }, "createrId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/id" }, "roleId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/id" }, "messageId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/id" }, "matchType": { "type": "string", @@ -486,56 +671,80 @@ } } }, - "tp_messages": { + "timePlanning": { "type": "object", - "required": ["channelId", "messageIds"], + "required": [ + "enabled", + "channelId", + "rolesEnabled", + "isAvailableRoleId", + "wantsToBeNotifieRoledId", + "messageIds" + ], "properties": { + "enabled": { + "type": "boolean" + }, "channelId": { - "type": "number", - "example": 1234567890123456789 + "$ref": "#/components/schemas/idOrNull" + }, + "rolesEnabled": { + "type": "boolean" + }, + "isAvailableRoleId": { + "$ref": "#/components/schemas/idOrNull" + }, + "wantsToBeNotifieRoledId": { + "$ref": "#/components/schemas/idOrNull" }, "messageIds": { "type": "object", "required": ["0", "1", "2", "3", "4", "5", "6"], "properties": { "0": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "1": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "2": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "3": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "4": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "5": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" }, "6": { - "type": "number", - "example": 1234567890123456789, - "nullable": true + "$ref": "#/components/schemas/idOrNull" } } } } + }, + "id": { + "type": "string", + "pattern": "^\\d{7,20}$", + "example": "1234567890123456789" + }, + "idOrNull": { + "type": "string", + "pattern": "^\\d{7,20}$", + "example": "1234567890123456789", + "nullable": true + }, + "error": { + "type": "object", + "required": "error", + "properties": { + "error": { + "type": "string" + } + } } }, "securitySchemes": { diff --git a/src/drizzle/schema.ts b/src/drizzle/schema.ts index 17d724b..cf96bab 100644 --- a/src/drizzle/schema.ts +++ b/src/drizzle/schema.ts @@ -1,7 +1,7 @@ import { relations } from "drizzle-orm"; import { + bigint, boolean, - integer, pgTable, primaryKey, serial, @@ -39,14 +39,16 @@ export const discordTokens = pgTable("tokens", { }); export const guilds = pgTable("guilds", { - id: integer("id").primaryKey(), + id: bigint("id", { mode: "bigint" }).primaryKey(), timezone: text("timezone").notNull().default("Etc/UTC"), tpEnabled: boolean("tp_enabled").notNull().default(false), - tpChannelId: integer("tp_channel_id"), - tpInterval: smallint("target_interval").notNull(), - tpRoles: boolean("tp_roles").notNull(), - isAvailableRoleId: integer("is_available_role_id"), - wantsToBeNotifieRoledId: integer("wants_to_be_notified_role_id"), + tpChannelId: bigint("tp_channel_id", { mode: "bigint" }), + tpInterval: smallint("target_interval").notNull().default(64), + tpRolesEnabled: boolean("tp_roles_enabled").notNull().default(false), + isAvailableRoleId: bigint("is_available_role_id", { mode: "bigint" }), + wantsToBeNotifieRoledId: bigint("wants_to_be_notified_role_id", { + mode: "bigint", + }), }); export const guildsRelations = relations(guilds, ({ many }) => ({ @@ -57,9 +59,9 @@ export const guildsRelations = relations(guilds, ({ many }) => ({ export const tpMessages = pgTable( "tp_messages", { - messageId: integer("message_id"), + messageId: bigint("message_id", { mode: "bigint" }), day: smallint("day").notNull(), - guildId: integer("guild_id") + guildId: bigint("guild_id", { mode: "bigint" }) .notNull() .references(() => guilds.id, { onDelete: "cascade" }), }, @@ -79,14 +81,14 @@ export const tpMessagesRelations = relations(tpMessages, ({ one }) => ({ export const matches = pgTable("matches", { id: serial("id").primaryKey(), - channelId: integer("channel_id").notNull(), + channelId: bigint("channel_id", { mode: "bigint" }).notNull(), matchType: varchar("match_type", { length: 50 }).notNull(), - createrId: integer("creater_id").notNull(), - roleId: integer("role_id").notNull(), + createrId: bigint("creater_id", { mode: "bigint" }).notNull(), + roleId: bigint("role_id", { mode: "bigint" }).notNull(), opponentName: varchar("opponent_name", { length: 100 }).notNull(), - messageId: integer("message_id").notNull(), + messageId: bigint("message_id", { mode: "bigint" }).notNull(), utc_ts: timestamp("utc_ts").notNull(), - guildId: integer("guild_id") + guildId: bigint("guild_id", { mode: "bigint" }) .notNull() .references(() => guilds.id, { onDelete: "cascade" }), }); diff --git a/src/lib/responseBuilders.ts b/src/lib/responseBuilders.ts index 105857f..fe8f4a6 100644 --- a/src/lib/responseBuilders.ts +++ b/src/lib/responseBuilders.ts @@ -2,14 +2,27 @@ import stringify from "json-stable-stringify"; import objectHash from "object-hash"; import { guilds, matches, tpMessages } from "~/drizzle/schema"; import { ExtractDataTypes, GetColumns } from "~/types/db"; +import { components } from "~/types/liljudd"; export const buildMatches = ( queryMatches: ExtractDataTypes>[], -) => +): components["schemas"]["match"][] => queryMatches.map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ id, guildId, utc_ts, ...match }) => ({ - ...match, + ({ + channelId, + createrId, + roleId, + messageId, + matchType, + opponentName, + utc_ts, + }) => ({ + channelId: channelId.toString(), + createrId: createrId.toString(), + roleId: roleId.toString(), + messageId: messageId.toString(), + matchType, + opponentName, utc_ts: utc_ts.toISOString(), }), ); @@ -19,14 +32,14 @@ export function buildConfig( tpMessages: ExtractDataTypes>[]; matches: ExtractDataTypes>[]; }, -) { +): components["schemas"]["guildConfig"] { const { id, timezone, tpEnabled, tpChannelId, tpInterval, - tpRoles, + tpRolesEnabled: tpRoles, isAvailableRoleId, wantsToBeNotifieRoledId, } = guildQuery; @@ -36,20 +49,26 @@ export function buildConfig( const targetDay = (tpInterval >> 11) & 7; const payload = { - guildId: id, + guildId: id.toString(), timezone, features: { timePlanning: { enabled: tpEnabled, - channelId: tpChannelId, + channelId: tpChannelId?.toString() ?? null, targetMinute, targetHour, targetDay, - roles: { enabled: tpRoles, isAvailableRoleId, wantsToBeNotifieRoledId }, + roles: { + enabled: tpRoles, + isAvailableRoleId: isAvailableRoleId?.toString() ?? null, + wantsToBeNotifieRoledId: wantsToBeNotifieRoledId?.toString() ?? null, + }, }, }, matches: buildMatches(guildQuery.matches), - checksum: objectHash(stringify(guildQuery)), }; - return payload; + + // generate checksum from payload because + // from guildQuery results in bigint serialization error + return { ...payload, checksum: objectHash(stringify(payload)) }; } diff --git a/src/lib/responses.ts b/src/lib/responses.ts index ac3bb37..4b4f541 100644 --- a/src/lib/responses.ts +++ b/src/lib/responses.ts @@ -12,6 +12,7 @@ export function ErrorResponse< M extends Methods

, C extends StatusCodes = StatusCodes, >(code: C, error?: string): APIResponse { + console.log(code, error); const responseData = { error: error ?? httpStatus[`${httpStatus[code]}_NAME`], }; diff --git a/src/lib/zod.ts b/src/lib/zod.ts index 17bfd68..13a85f4 100644 --- a/src/lib/zod.ts +++ b/src/lib/zod.ts @@ -1,13 +1,17 @@ import moment from "moment-timezone"; import { z } from "zod"; -export const zodId = z +const zodId = z .string() - .refine((value) => /^\d{7,20}$/.test(value), "Invalid ID supplied") - .transform((value) => parseInt(value)); + .refine((value) => /^\d{7,20}$/.test(value), "Invalid ID supplied"); +export const zodBigIntId = zodId.transform((value) => BigInt(value)); export const zodTpMessages = z.object({ - channelId: zodId, + enabled: z.boolean(), + channelId: zodId.nullable(), + rolesEnabled: z.boolean(), + isAvailableRoleId: zodId.nullable(), + wantsToBeNotifieRoledId: zodId.nullable(), messageIds: z.object({ "0": zodId.nullable(), "1": zodId.nullable(), diff --git a/src/middleware.ts b/src/middleware.ts index 6fb20e1..d344e7e 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,8 +1,14 @@ import { createMiddleware } from "@solidjs/start/middleware"; -import { Session, User, verifyRequestOrigin } from "lucia"; +import colors from "colors"; +import fs from "fs"; +import { verifyRequestOrigin } from "lucia"; import { appendHeader, getCookie, getHeader } from "vinxi/http"; import { lucia } from "./lib/auth"; +colors.enable(); + +let started: boolean = false; + export default createMiddleware({ onRequest: async (event) => { if (event.nativeEvent.node.req.method !== "GET") { @@ -43,12 +49,52 @@ export default createMiddleware({ event.nativeEvent.context.session = session; event.nativeEvent.context.user = user; }, -}); + onBeforeResponse: async (event, response) => { + let consoleLog = "", + fileLog = ""; -declare module "h3" { - // eslint-disable-next-line no-unused-vars - interface H3EventContext { - user: User | null; - session: Session | null; - } -} + if (!started) { + try { + await fs.promises.mkdir("log"); + console.log("Created 'log' Folder."); + } catch {} + started = true; + } + + const currentDate = new Date(); + const year = currentDate.getFullYear(); + const month = String(currentDate.getMonth() + 1).padStart(2, "0"); + const day = String(currentDate.getDate()).padStart(2, "0"); + const hours = String(currentDate.getHours()).padStart(2, "0"); + const minutes = String(currentDate.getMinutes()).padStart(2, "0"); + const seconds = String(currentDate.getSeconds()).padStart(2, "0"); + + // Create a short and numeric representation + const date = `[${year}-${month}-${day}_${hours}:${minutes}:${seconds}]`; + const xForwardedFor = event.request.headers.get("x-forwarded-for"); + const ip = (xForwardedFor || "127.0.0.1, 192.168.178.1").split(","); + const route = event.request.url; + const frontend = !new URL(event.request.url).pathname.startsWith("/api"); + const method = frontend ? "Frontend" : event.request.method; + const code = + (response.body as Response | undefined)?.status ?? event.response.status; + consoleLog += [ + date, + ip[0].yellow, + method, + code, + route?.green, + event.nativeEvent.context.user?.discord_id.rainbow, + ].join(" "); + fileLog += [ + date, + ip[0], + method, + code, + route, + event.nativeEvent.context.user?.discord_id, + ].join(" "); + await fs.promises.appendFile("log/log.txt", fileLog + "\n"); + console.log(consoleLog); + }, +}); diff --git a/src/routes/api/[guildId]/config.ts b/src/routes/api/[guildId]/config.ts index 6655999..ad269ee 100644 --- a/src/routes/api/[guildId]/config.ts +++ b/src/routes/api/[guildId]/config.ts @@ -5,7 +5,7 @@ import { guilds } from "~/drizzle/schema"; import { BasicAuth } from "~/lib/auth"; import { buildConfig } from "~/lib/responseBuilders"; import { ErrorResponse, Res } from "~/lib/responses"; -import { zodId } from "~/lib/zod"; +import { zodBigIntId } from "~/lib/zod"; import { APIResponse } from "~/types/backend"; type Path = "/api/{guildId}/config"; @@ -22,9 +22,9 @@ export const GET = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } @@ -41,6 +41,32 @@ export const GET = async ( return Res("OK", buildConfig(guildQuery)); }; +export const POST = async ( + event: APIEvent, +): Promise> => { + switch (event.request.headers.get("authorization")) { + case BasicAuth.unencoded: + case BasicAuth.encoded: + break; + + default: + return ErrorResponse("UNAUTHORIZED"); + } + + let guildId: bigint; + try { + guildId = zodBigIntId.parse(event.params.guildId); + } catch (e) { + return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); + } + + const guildQuery = await db.insert(guilds).values({ id: guildId }).execute(); + + if (!guildQuery) return ErrorResponse("NOT_FOUND"); + + return Res("NO_CONTENT", null); +}; + export const DELETE = async ( event: APIEvent, ): Promise> => { @@ -53,9 +79,9 @@ export const DELETE = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } diff --git a/src/routes/api/[guildId]/matches.ts b/src/routes/api/[guildId]/matches.ts index 6ad4195..c47ddd3 100644 --- a/src/routes/api/[guildId]/matches.ts +++ b/src/routes/api/[guildId]/matches.ts @@ -5,7 +5,7 @@ import { guilds, matches } from "~/drizzle/schema"; import { BasicAuth } from "~/lib/auth"; import { buildMatches } from "~/lib/responseBuilders"; import { ErrorResponse, Res } from "~/lib/responses"; -import { zodId, zodMatch } from "~/lib/zod"; +import { zodBigIntId, zodMatch } from "~/lib/zod"; import { APIResponse, RequestBody } from "~/types/backend"; type Path = "/api/{guildId}/matches"; @@ -22,9 +22,9 @@ export const GET = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } @@ -60,9 +60,9 @@ export const POST = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } @@ -94,8 +94,13 @@ export const POST = async ( ); await db.insert(matches).values({ - ...body.match, guildId: guild.id, + channelId: BigInt(body.match.channelId), + roleId: BigInt(body.match.roleId), + createrId: BigInt(body.match.createrId), + messageId: BigInt(body.match.messageId), + matchType: body.match.matchType, + opponentName: body.match.opponentName, utc_ts: new Date(body.match.utc_ts), }); diff --git a/src/routes/api/[guildId]/tp_messages.ts b/src/routes/api/[guildId]/timePlanning.ts similarity index 67% rename from src/routes/api/[guildId]/tp_messages.ts rename to src/routes/api/[guildId]/timePlanning.ts index 043f63e..ddf4255 100644 --- a/src/routes/api/[guildId]/tp_messages.ts +++ b/src/routes/api/[guildId]/timePlanning.ts @@ -4,14 +4,14 @@ import db from "~/drizzle"; import { guilds, tpMessages } from "~/drizzle/schema"; import { BasicAuth } from "~/lib/auth"; import { ErrorResponse, Res } from "~/lib/responses"; -import { zodId, zodTpMessages } from "~/lib/zod"; +import { zodBigIntId, zodTpMessages } from "~/lib/zod"; import { APIResponse, RequestBody } from "~/types/backend"; -type Path = "/api/{guildId}/tp_messages"; +type Path = "/api/{guildId}/timePlanning"; const DayKeys = ["0", "1", "2", "3", "4", "5", "6"] as const; type DayKeys = (typeof DayKeys)[number]; -type Messages = Record; +type Messages = Record; export const GET = async ( event: APIEvent, @@ -25,9 +25,9 @@ export const GET = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } @@ -41,13 +41,11 @@ export const GET = async ( if (!guild) return ErrorResponse("NOT_FOUND"); - if (!guild.tpEnabled || !guild.tpChannelId) return Res("NO_CONTENT", null); - const tpMessages = guild.tpMessages.reduce( (acc, message) => { const day = message.day.toString() as DayKeys; if (!/^[0-6]$/.test(day)) return acc; - acc[day] = message.messageId; + acc[day] = message.messageId?.toString() ?? null; return acc; }, { @@ -62,7 +60,11 @@ export const GET = async ( ); return Res("OK", { - channelId: guild.tpChannelId, + enabled: guild.tpEnabled, + channelId: guild.tpChannelId?.toString() ?? null, + rolesEnabled: guild.tpRolesEnabled, + isAvailableRoleId: guild.isAvailableRoleId?.toString() ?? null, + wantsToBeNotifieRoledId: guild.wantsToBeNotifieRoledId?.toString() ?? null, messageIds: tpMessages, }); }; @@ -79,9 +81,9 @@ export const PUT = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } @@ -95,8 +97,6 @@ export const PUT = async ( if (!guild) return ErrorResponse("NOT_FOUND"); - if (!guild.tpEnabled) return ErrorResponse("FORBIDDEN"); - const unparsedBody = await new Response(event.request.body).json(); let body: RequestBody; @@ -106,19 +106,36 @@ export const PUT = async ( return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } - if (guild.tpChannelId !== body.channelId) + const { + enabled, + channelId, + rolesEnabled, + isAvailableRoleId, + wantsToBeNotifieRoledId, + messageIds, + } = body; + if (guild.tpChannelId !== channelId) await db .update(guilds) - .set({ tpChannelId: body.channelId }) + .set({ + tpEnabled: enabled, + tpChannelId: channelId ? BigInt(channelId) : null, + tpRolesEnabled: rolesEnabled, + isAvailableRoleId: isAvailableRoleId ? BigInt(isAvailableRoleId) : null, + wantsToBeNotifieRoledId: wantsToBeNotifieRoledId + ? BigInt(wantsToBeNotifieRoledId) + : null, + }) .where(eq(guilds.id, guild.id)) .execute(); await Promise.all( DayKeys.map(async (dayStr) => { const day = parseInt(dayStr); + const messageId = messageIds[dayStr]; await db .update(tpMessages) - .set({ messageId: body.messageIds[dayStr] }) + .set({ messageId: messageId ? BigInt(messageId) : null }) .where(and(eq(tpMessages.guildId, guild.id), eq(tpMessages.day, day))) .execute(); }), diff --git a/src/routes/api/boot.ts b/src/routes/api/boot.ts index 26ef143..fdd375b 100644 --- a/src/routes/api/boot.ts +++ b/src/routes/api/boot.ts @@ -5,7 +5,7 @@ import { guilds } from "~/drizzle/schema"; import { BasicAuth } from "~/lib/auth"; import { buildConfig } from "~/lib/responseBuilders"; import { ErrorResponse, Res } from "~/lib/responses"; -import { zodId } from "~/lib/zod"; +import { zodBigIntId } from "~/lib/zod"; import { APIResponse } from "~/types/backend"; type Path = "/api/boot"; @@ -22,9 +22,9 @@ export const GET = async ( return ErrorResponse("UNAUTHORIZED"); } - let guildId: number; + let guildId: bigint; try { - guildId = zodId.parse(event.params.guildId); + guildId = zodBigIntId.parse(event.params.guildId); } catch (e) { return ErrorResponse("BAD_REQUEST", JSON.stringify(e)); } diff --git a/src/types/db.d.ts b/src/types/db.d.ts index bb6a905..7be0310 100644 --- a/src/types/db.d.ts +++ b/src/types/db.d.ts @@ -6,6 +6,8 @@ export type GetColumns = export type ExtractDataTypes = { // eslint-disable-next-line @typescript-eslint/no-explicit-any [K in keyof T]: T[K] extends PgColumn - ? ColumnConfig["data"] + ? ColumnConfig["notNull"] extends true + ? ColumnConfig["data"] + : ColumnConfig["data"] | null : unknown; }; diff --git a/src/types/env.d.ts b/src/types/env.d.ts index ea72b54..98ff518 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -12,7 +12,6 @@ interface ImportMetaEnv { readonly VITE_DATABASE_URL: string; } -// eslint-disable-next-line no-unused-vars interface ImportMeta { readonly env: ImportMetaEnv; } diff --git a/src/types/liljudd.d.ts b/src/types/liljudd.d.ts index 6e255b6..3aeb1ca 100644 --- a/src/types/liljudd.d.ts +++ b/src/types/liljudd.d.ts @@ -18,33 +18,38 @@ export interface paths { * @description Returns a single guild's config. */ get: operations["getGuildById"]; + /** + * Creates a guild's config by ID + * @description Create a guild's config when the bot is has joined a new guild. + */ + post: operations["postGuildById"]; /** * Deletes a guild's config by ID * @description Delete a guild's config when the bot is removed from the guild. */ delete: operations["deleteGuildById"]; }; - "/api/{guildId}/tp_messages": { + "/api/{guildId}/timePlanning": { /** - * Find the tp_messages of guild by ID - * @description Returns tp_messages for a guild + * Find the timePlanning of guild by ID + * @description Returns timePlanning for a guild */ - get: operations["getTp_messagesOfGuildById"]; + get: operations["gettimePlanningOfGuildById"]; /** - * Put new message IDs for tp_messages of guild by ID - * @description Returns tp_messages for a guild + * Put new message IDs for timePlanning of guild by ID + * @description Returns timePlanning for a guild */ - put: operations["putTp_messagesOfGuildById"]; + put: operations["puttimePlanningOfGuildById"]; }; "/api/{guildId}/matches": { /** * Find all matches of guild by ID - * @description Returns tp_messages for a guild + * @description Returns timePlanning for a guild */ get: operations["getMatchesOfGuildById"]; /** * Save a new created match of guild by ID - * @description Returns tp_messages for a guild + * @description Returns timePlanning for a guild */ post: operations["postMatchOfGuildById"]; }; @@ -55,8 +60,7 @@ export type webhooks = Record; export interface components { schemas: { guildConfig: { - /** @example 1234567890123456800 */ - guildId: number; + guildId: components["schemas"]["id"]; /** * Format: text * @example Europe/Berlin @@ -65,8 +69,7 @@ export interface components { features: { timePlanning: { enabled: boolean; - /** @example 1234567890123456800 */ - channelId: number | null; + channelId: components["schemas"]["idOrNull"]; /** @example 0 */ targetMinute: number; /** @example 1 */ @@ -75,10 +78,8 @@ export interface components { targetDay: number; roles: { enabled: boolean; - /** @example 1234567890123456800 */ - isAvailableRoleId: number | null; - /** @example 1234567890123456800 */ - wantsToBeNotifieRoledId: number | null; + isAvailableRoleId: components["schemas"]["idOrNull"]; + wantsToBeNotifieRoledId: components["schemas"]["idOrNull"]; }; }; }; @@ -86,14 +87,10 @@ export interface components { checksum: string; }; match: { - /** @example 1234567890123456800 */ - channelId: number; - /** @example 1234567890123456800 */ - createrId: number; - /** @example 1234567890123456800 */ - roleId: number; - /** @example 1234567890123456800 */ - messageId: number; + channelId: components["schemas"]["id"]; + createrId: components["schemas"]["id"]; + roleId: components["schemas"]["id"]; + messageId: components["schemas"]["id"]; /** * Format: varchar(50) * @example Scrim @@ -107,26 +104,29 @@ export interface components { /** @example 2020-01-01T00:00:00Z */ utc_ts: string; }; - tp_messages: { - /** @example 1234567890123456800 */ - channelId: number; + timePlanning: { + enabled: boolean; + channelId: components["schemas"]["idOrNull"]; + rolesEnabled: boolean; + isAvailableRoleId: components["schemas"]["idOrNull"]; + wantsToBeNotifieRoledId: components["schemas"]["idOrNull"]; messageIds: { - /** @example 1234567890123456800 */ - 0: number | null; - /** @example 1234567890123456800 */ - 1: number | null; - /** @example 1234567890123456800 */ - 2: number | null; - /** @example 1234567890123456800 */ - 3: number | null; - /** @example 1234567890123456800 */ - 4: number | null; - /** @example 1234567890123456800 */ - 5: number | null; - /** @example 1234567890123456800 */ - 6: number | null; + 0: components["schemas"]["idOrNull"]; + 1: components["schemas"]["idOrNull"]; + 2: components["schemas"]["idOrNull"]; + 3: components["schemas"]["idOrNull"]; + 4: components["schemas"]["idOrNull"]; + 5: components["schemas"]["idOrNull"]; + 6: components["schemas"]["idOrNull"]; }; }; + /** @example 1234567890123456789 */ + id: string; + /** @example 1234567890123456789 */ + idOrNull: string | null; + error: { + error?: string; + }; }; responses: never; parameters: never; @@ -155,15 +155,21 @@ export interface operations { }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; @@ -187,16 +193,58 @@ export interface operations { }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { + content: { + "application/json": components["schemas"]["error"]; + }; + }; + }; + }; + /** + * Creates a guild's config by ID + * @description Create a guild's config when the bot is has joined a new guild. + */ + postGuildById: { + parameters: { + path: { + /** @description ID of guild's config to create */ + guildId: string; + }; + }; + responses: { + /** @description successful operation */ + 204: { content: never; }; + /** @description Invalid ID supplied */ + 400: { + content: { + "application/json": components["schemas"]["error"]; + }; + }; + /** @description Unauthorized */ + 401: { + content: { + "application/json": components["schemas"]["error"]; + }; + }; + /** @description Guild not found */ + 404: { + content: { + "application/json": components["schemas"]["error"]; + }; + }; }; }; /** @@ -206,7 +254,7 @@ export interface operations { deleteGuildById: { parameters: { path: { - /** @description ID of guild config to delete */ + /** @description ID of guild's config to delete */ guildId: string; }; }; @@ -217,26 +265,32 @@ export interface operations { }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; /** - * Find the tp_messages of guild by ID - * @description Returns tp_messages for a guild + * Find the timePlanning of guild by ID + * @description Returns timePlanning for a guild */ - getTp_messagesOfGuildById: { + gettimePlanningOfGuildById: { parameters: { path: { - /** @description ID of guild's tp_messages to return */ + /** @description ID of guild's timePlanning to return */ guildId: string; }; }; @@ -244,42 +298,44 @@ export interface operations { /** @description successful operation */ 200: { content: { - "application/json": components["schemas"]["tp_messages"]; + "application/json": components["schemas"]["timePlanning"]; }; }; - /** @description Time planning not enabled for this guild */ - 204: { - content: never; - }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; /** - * Put new message IDs for tp_messages of guild by ID - * @description Returns tp_messages for a guild + * Put new message IDs for timePlanning of guild by ID + * @description Returns timePlanning for a guild */ - putTp_messagesOfGuildById: { + puttimePlanningOfGuildById: { parameters: { path: { - /** @description ID of guild's tp_messages to return */ + /** @description ID of guild's timePlanning to return */ guildId: string; }; }; - /** @description Put new message IDs for tp_messages in channel */ + /** @description Put new message IDs for timePlanning in channel */ requestBody: { content: { - "application/json": components["schemas"]["tp_messages"]; + "application/json": components["schemas"]["timePlanning"]; }; }; responses: { @@ -289,30 +345,32 @@ export interface operations { }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; - }; - /** @description Time planning not enabled for this guild */ - 403: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; /** * Find all matches of guild by ID - * @description Returns tp_messages for a guild + * @description Returns timePlanning for a guild */ getMatchesOfGuildById: { parameters: { path: { - /** @description ID of guild's tp_messages to return */ + /** @description ID of guild's timePlanning to return */ guildId: string; }; }; @@ -330,27 +388,29 @@ export interface operations { }; }; }; - /** @description Time planning not enabled for this guild */ - 204: { - content: never; - }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; /** * Save a new created match of guild by ID - * @description Returns tp_messages for a guild + * @description Returns timePlanning for a guild */ postMatchOfGuildById: { parameters: { @@ -380,15 +440,21 @@ export interface operations { }; /** @description Invalid ID supplied */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Unauthorized */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; /** @description Guild not found */ 404: { - content: never; + content: { + "application/json": components["schemas"]["error"]; + }; }; }; }; diff --git a/src/types/lucia-auth.d.ts b/src/types/lucia-auth.d.ts index 880f727..c998aa0 100644 --- a/src/types/lucia-auth.d.ts +++ b/src/types/lucia-auth.d.ts @@ -3,7 +3,6 @@ import { lucia } from "~/lib/auth"; import { ExtractDataTypes, GetColumns } from "./db"; declare module "lucia" { - // eslint-disable-next-line no-unused-vars interface Register { Lucia: typeof lucia; DatabaseUserAttributes: DatabaseUserAttributes; @@ -11,6 +10,4 @@ declare module "lucia" { } interface DatabaseUserAttributes - extends ExtractDataTypes> { - warst: string; -} + extends ExtractDataTypes> {} diff --git a/src/types/vinxi.d.ts b/src/types/vinxi.d.ts new file mode 100644 index 0000000..266cfa9 --- /dev/null +++ b/src/types/vinxi.d.ts @@ -0,0 +1,12 @@ +import { H3EventContext as EventContext } from "h3/dist"; +import { Session, User } from "lucia"; + +declare module "vinxi/http" { + interface H3EventContext extends EventContext { + user: User | null; + session: Session | null; + } + class H3Eventt { + context: H3EventContext; + } +}