Fix: Using bigint and added backend testing
This commit is contained in:
parent
ed6195e1e2
commit
89507f8412
20 changed files with 830 additions and 292 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
src/drizzle/migrations
|
||||
log
|
||||
|
||||
dist
|
||||
.vinxi
|
||||
|
|
|
@ -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
|
166
e2e/auth.spec.ts
166
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<paths>({
|
||||
test("Generate auth session for further tests", async ({ browser }) => {
|
||||
const { GET } = createClient<discord.paths>({
|
||||
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<liljudd.paths>({
|
||||
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,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: '*'
|
||||
|
|
|
@ -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,54 +671,78 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"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,
|
||||
"$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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }),
|
||||
});
|
||||
|
|
|
@ -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<GetColumns<typeof matches>>[],
|
||||
) =>
|
||||
): 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<GetColumns<typeof tpMessages>>[];
|
||||
matches: ExtractDataTypes<GetColumns<typeof matches>>[];
|
||||
},
|
||||
) {
|
||||
): 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)) };
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ export function ErrorResponse<
|
|||
M extends Methods<P>,
|
||||
C extends StatusCodes<P, M> = StatusCodes<P, M>,
|
||||
>(code: C, error?: string): APIResponse<P, M> {
|
||||
console.log(code, error);
|
||||
const responseData = {
|
||||
error: error ?? httpStatus[`${httpStatus[code]}_NAME`],
|
||||
};
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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<APIResponse<Path, "post">> => {
|
||||
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<APIResponse<Path, "delete">> => {
|
||||
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
|
||||
|
|
|
@ -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<DayKeys, number | null>;
|
||||
type Messages = Record<DayKeys, string | null>;
|
||||
|
||||
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<Path, "put">;
|
||||
|
@ -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();
|
||||
}),
|
|
@ -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));
|
||||
}
|
||||
|
|
2
src/types/db.d.ts
vendored
2
src/types/db.d.ts
vendored
|
@ -6,6 +6,8 @@ export type GetColumns<T> =
|
|||
export type ExtractDataTypes<T> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[K in keyof T]: T[K] extends PgColumn<infer ColumnConfig, any, any>
|
||||
? ColumnConfig["notNull"] extends true
|
||||
? ColumnConfig["data"]
|
||||
: ColumnConfig["data"] | null
|
||||
: unknown;
|
||||
};
|
||||
|
|
1
src/types/env.d.ts
vendored
1
src/types/env.d.ts
vendored
|
@ -12,7 +12,6 @@ interface ImportMetaEnv {
|
|||
readonly VITE_DATABASE_URL: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
|
242
src/types/liljudd.d.ts
vendored
242
src/types/liljudd.d.ts
vendored
|
@ -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<string, never>;
|
|||
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;
|
||||
content: {
|
||||
"application/json": components["schemas"]["error"];
|
||||
};
|
||||
/** @description Time planning not enabled for this guild */
|
||||
403: {
|
||||
content: never;
|
||||
};
|
||||
/** @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"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
5
src/types/lucia-auth.d.ts
vendored
5
src/types/lucia-auth.d.ts
vendored
|
@ -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<GetColumns<typeof users>> {
|
||||
warst: string;
|
||||
}
|
||||
extends ExtractDataTypes<GetColumns<typeof users>> {}
|
||||
|
|
12
src/types/vinxi.d.ts
vendored
Normal file
12
src/types/vinxi.d.ts
vendored
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue