diff --git a/.env.example b/.env.example
index a126d1b..3b2e6f6 100644
--- a/.env.example
+++ b/.env.example
@@ -2,7 +2,8 @@
DOMAIN=localhost
ORIGIN=http://$DOMAIN:5173
-NODE_ENV=development
+NODE_ENV=
+LOG_LEVEL=
DATABASE_USER='postgres'
DATABASE_PASSWORD='postgres'
@@ -32,7 +33,6 @@ PUBLIC_SITE_URL='http://$DOMAIN:5173'
PUBLIC_UMAMI_DO_NOT_TRACK=true
PUBLIC_UMAMI_URL=
PUBLIC_UMAMI_ID=
-PUBLIC_PLAUSIBLE_URL=
# quick setting for key-combo only
SVELTE_INSPECTOR_TOGGLE=control-shift-i
diff --git a/package.json b/package.json
index 1371b65..1e9fc78 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "boredgame",
- "version": "0.0.2",
+ "version": "0.0.5",
"private": "true",
"scripts": {
"db:push": "drizzle-kit push",
@@ -30,10 +30,10 @@
"@playwright/test": "^1.47.1",
"@sveltejs/adapter-auto": "^3.2.5",
"@sveltejs/enhanced-img": "^0.3.8",
- "@sveltejs/kit": "^2.6.2",
+ "@sveltejs/kit": "^2.6.3",
"@sveltejs/vite-plugin-svelte": "4.0.0-next.7",
"@types/cookie": "^0.6.0",
- "@types/node": "^20.16.10",
+ "@types/node": "^20.16.11",
"@types/pg": "^8.11.10",
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^7.18.0",
@@ -92,12 +92,13 @@
"@oslojs/otp": "^1.0.0",
"@oslojs/webauthn": "^1.0.0",
"@paralleldrive/cuid2": "^2.2.2",
+ "@scalar/hono-api-reference": "^0.5.152",
"@sveltejs/adapter-node": "^5.2.5",
- "@sveltejs/adapter-vercel": "^5.4.4",
+ "@sveltejs/adapter-vercel": "^5.4.5",
"@types/feather-icons": "^4.29.4",
"bits-ui": "^0.21.16",
"boardgamegeekclient": "^1.9.1",
- "bullmq": "^5.16.0",
+ "bullmq": "^5.17.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cookie": "^0.6.0",
@@ -111,6 +112,7 @@
"hono": "^4.6.3",
"hono-pino": "^0.3.0",
"hono-rate-limiter": "^0.4.0",
+ "hono-zod-openapi": "^0.2.0",
"html-entities": "^2.5.2",
"iconify-icon": "^2.1.0",
"ioredis": "^5.4.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 85ebb3c..2e860d5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,12 +62,15 @@ importers:
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
+ '@scalar/hono-api-reference':
+ specifier: ^0.5.152
+ version: 0.5.152(hono@4.6.3)
'@sveltejs/adapter-node':
specifier: ^5.2.5
- version: 5.2.5(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))
+ version: 5.2.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))
'@sveltejs/adapter-vercel':
- specifier: ^5.4.4
- version: 5.4.4(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))
+ specifier: ^5.4.5
+ version: 5.4.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))
'@types/feather-icons':
specifier: ^4.29.4
version: 4.29.4
@@ -78,8 +81,8 @@ importers:
specifier: ^1.9.1
version: 1.9.1
bullmq:
- specifier: ^5.16.0
- version: 5.16.0
+ specifier: ^5.17.1
+ version: 5.17.1
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -106,7 +109,7 @@ importers:
version: 4.29.2
formsnap:
specifier: ^1.0.1
- version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175))
+ version: 1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175))
handlebars:
specifier: ^4.7.8
version: 4.7.8
@@ -119,6 +122,9 @@ importers:
hono-rate-limiter:
specifier: ^0.4.0
version: 0.4.0(hono@4.6.3)
+ hono-zod-openapi:
+ specifier: ^0.2.0
+ version: 0.2.0(hono@4.6.3)(zod@3.23.8)
html-entities:
specifier: ^2.5.2
version: 2.5.2
@@ -184,10 +190,10 @@ importers:
version: 2.5.3
tailwind-variants:
specifier: ^0.2.1
- version: 0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)))
+ version: 0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))
tailwindcss-animate:
specifier: ^1.0.7
- version: 1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)))
+ version: 1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))
tsyringe:
specifier: ^4.8.0
version: 4.8.0
@@ -212,22 +218,22 @@ importers:
version: 1.47.2
'@sveltejs/adapter-auto':
specifier: ^3.2.5
- version: 3.2.5(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))
+ version: 3.2.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))
'@sveltejs/enhanced-img':
specifier: ^0.3.8
- version: 0.3.8(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ version: 0.3.8(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
'@sveltejs/kit':
- specifier: ^2.6.2
- version: 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ specifier: ^2.6.3
+ version: 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
'@sveltejs/vite-plugin-svelte':
specifier: 4.0.0-next.7
- version: 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ version: 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
'@types/cookie':
specifier: ^0.6.0
version: 0.6.0
'@types/node':
- specifier: ^20.16.10
- version: 20.16.10
+ specifier: ^20.16.11
+ version: 20.16.11
'@types/pg':
specifier: ^8.11.10
version: 8.11.10
@@ -257,7 +263,7 @@ importers:
version: 9.1.0(eslint@8.57.1)
eslint-plugin-svelte:
specifier: 2.36.0-next.13
- version: 2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ version: 2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
just-clone:
specifier: ^6.2.0
version: 6.2.0
@@ -311,16 +317,16 @@ importers:
version: 2.0.2
sveltekit-flash-message:
specifier: ^2.4.4
- version: 2.4.4(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)
+ version: 2.4.4(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)
sveltekit-superforms:
specifier: ^2.19.1
- version: 2.19.1(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)
+ version: 2.19.1(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)
tailwindcss:
specifier: ^3.4.13
- version: 3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ version: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@20.16.10)(typescript@5.6.2)
+ version: 10.9.2(@types/node@20.16.11)(typescript@5.6.2)
tslib:
specifier: ^2.7.0
version: 2.7.0
@@ -332,10 +338,10 @@ importers:
version: 5.6.2
vite:
specifier: ^5.4.8
- version: 5.4.8(@types/node@20.16.10)
+ version: 5.4.8(@types/node@20.16.11)
vitest:
specifier: ^1.6.0
- version: 1.6.0(@types/node@20.16.10)
+ version: 1.6.0(@types/node@20.16.11)
zod:
specifier: ^3.23.8
version: 3.23.8
@@ -1447,6 +1453,12 @@ packages:
hono: '>=3.9.0'
zod: ^3.19.1
+ '@hono/zod-validator@0.4.1':
+ resolution: {integrity: sha512-I8LyfeJfvVmC5hPjZ2Iij7RjexlgSBT7QJudZ4JvNPLxn0JQ3sqclz2zydlwISAnw21D2n4LQ0nfZdoiv9fQQA==}
+ peerDependencies:
+ hono: '>=3.9.0'
+ zod: ^3.19.1
+
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -2131,6 +2143,20 @@ packages:
cpu: [x64]
os: [win32]
+ '@scalar/hono-api-reference@0.5.152':
+ resolution: {integrity: sha512-mSRpicEjr3q3ulXHV9fpPKgq+Mju0/A9jyB4NvvZ9GitunTm6P4urr/RKzgESDTqYK3zK/VGahZWeQFLi6PSKQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ hono: ^4.0.0
+
+ '@scalar/openapi-types@0.1.1':
+ resolution: {integrity: sha512-NMy3QNk6ytcCoPUGJH0t4NNr36OWXgZhA3ormr3TvhX1NDgoF95wFyodGVH8xiHeUyn2/FxtETm8UBLbB5xEmg==}
+ engines: {node: '>=18'}
+
+ '@scalar/types@0.0.13':
+ resolution: {integrity: sha512-4baCQ3uXTQsT/da5X3+yO34VEu057Jjw0SJFkaaFmXTqHXVZM+SaDHSyTvT6tyKvgDpPpn6iD22uiFCdldEzRQ==}
+ engines: {node: '>=18'}
+
'@sideway/address@4.1.5':
resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==}
@@ -2156,8 +2182,8 @@ packages:
peerDependencies:
'@sveltejs/kit': ^2.4.0
- '@sveltejs/adapter-vercel@5.4.4':
- resolution: {integrity: sha512-KORoxxqB2H5DrxpCHc9Yfijcgvmoaaz6G6eKHEg9fRlTsujJkxN26C0x4YlcgxqDU4dLIi1wfSLHpuZD0E4Irg==}
+ '@sveltejs/adapter-vercel@5.4.5':
+ resolution: {integrity: sha512-SROpUbjSZ1Xni4xuS22dunXFLjYzvTZwppqixIQFzTrf9oWcZEm2OfO+VgnrOT67LOcWQefJp7VSrpmjV691yQ==}
peerDependencies:
'@sveltejs/kit': ^2.4.0
@@ -2167,8 +2193,8 @@ packages:
svelte: ^4.0.0 || ^5.0.0-next.0
vite: '>= 5.0.0'
- '@sveltejs/kit@2.6.2':
- resolution: {integrity: sha512-ruogrSPXjckn5poUiZU8VYNCSPHq66SFR1AATvOikQxtP6LNI4niAZVX/AWZRe/EPDG3oY2DNJ9c5z7u0t2NAQ==}
+ '@sveltejs/kit@2.6.3':
+ resolution: {integrity: sha512-baIAnmfMqAISrPtTC/22w6ay5kTEIQ/vq9bctiaQgRIoLCPBNhb6LEidTuWQS7OzPYCDBMuMX1t/fMvi4r3q/g==}
engines: {node: '>=18.13'}
hasBin: true
peerDependencies:
@@ -2224,8 +2250,8 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
- '@types/node@20.16.10':
- resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==}
+ '@types/node@20.16.11':
+ resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==}
'@types/pg@8.11.10':
resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==}
@@ -2322,6 +2348,9 @@ packages:
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ '@unhead/schema@1.11.7':
+ resolution: {integrity: sha512-j9uN7T63aUXrZ6yx2CfjVT7xZHjn0PZO7TPMaWqMFjneIH/NONKvDVCMEqDlXeqdSIERIYtk/xTHgCUMer5eyw==}
+
'@vercel/nft@0.27.4':
resolution: {integrity: sha512-Rioz3LJkEKicKCi9BSyc1RXZ5R6GmXosFMeBSThh6msWSOiArKhb7c75MiWwZEgPL7x0/l3TAfH/l0cxKNuUFA==}
engines: {node: '>=16'}
@@ -2525,8 +2554,8 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
- bullmq@5.16.0:
- resolution: {integrity: sha512-7FaZzHsRXFOxrxCQTNKowuo9PPRwnAOpYXB5tcfk8vg0IbuVQ/je1Bf228Zy29gCS/5ytIEJNVB/DDGwZM0wbA==}
+ bullmq@5.17.1:
+ resolution: {integrity: sha512-wqjhdJptb3KLeuUO+ZfsjVHqHuPtHhhoAE2XM8zvnidk6uU/ELo16uU6CVTBhliAupgvPE9jWE7tchR0GCtsdA==}
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
@@ -2657,10 +2686,6 @@ packages:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
engines: {node: '>= 0.6'}
- cookie@0.7.2:
- resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
- engines: {node: '>= 0.6'}
-
core-js@3.38.1:
resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==}
@@ -3312,10 +3337,19 @@ packages:
peerDependencies:
hono: ^4.1.1
+ hono-zod-openapi@0.2.0:
+ resolution: {integrity: sha512-LYQXLb+shxIdS5PLFAaeelQg5BQ3rph5kB1rpUbqRW4Pr4JxHA8u7sjNNs0g7H/sXOiFLNAZRhF3yFsgBEoybA==}
+ peerDependencies:
+ hono: ^4.6.3
+ zod: ^3.21.4
+
hono@4.6.3:
resolution: {integrity: sha512-0LeEuBNFeSHGqZ9sNVVgZjB1V5fmhkBSB0hZrpqStSMLOWgfLy0dHOvrjbJh0H2khsjet6rbHfWTHY0kpYThKQ==}
engines: {node: '>=16.9.0'}
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
html-entities@2.5.2:
resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==}
@@ -5110,9 +5144,18 @@ packages:
yup@1.4.0:
resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==}
+ zhead@2.2.4:
+ resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==}
+
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
+ zod-openapi@3.1.1:
+ resolution: {integrity: sha512-jn5udnrGTAmTY7nj/E6ggKBsqIi9Vzj5u0RQpDyOOpzmz2ijcBssSeEjnQFbriqG2PY2JJBP5Gi4umRrhVxghQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.21.4
+
zod-to-json-schema@3.23.3:
resolution: {integrity: sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==}
peerDependencies:
@@ -5885,6 +5928,11 @@ snapshots:
hono: 4.6.3
zod: 3.23.8
+ '@hono/zod-validator@0.4.1(hono@4.6.3)(zod@3.23.8)':
+ dependencies:
+ hono: 4.6.3
+ zod: 3.23.8
+
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -6459,6 +6507,18 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.24.0':
optional: true
+ '@scalar/hono-api-reference@0.5.152(hono@4.6.3)':
+ dependencies:
+ '@scalar/types': 0.0.13
+ hono: 4.6.3
+
+ '@scalar/openapi-types@0.1.1': {}
+
+ '@scalar/types@0.0.13':
+ dependencies:
+ '@scalar/openapi-types': 0.1.1
+ '@unhead/schema': 1.11.7
+
'@sideway/address@4.1.5':
dependencies:
'@hapi/hoek': 9.3.0
@@ -6475,43 +6535,43 @@ snapshots:
'@sinclair/typebox@0.32.35':
optional: true
- '@sveltejs/adapter-auto@3.2.5(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))':
+ '@sveltejs/adapter-auto@3.2.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))':
dependencies:
- '@sveltejs/kit': 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/kit': 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
import-meta-resolve: 4.1.0
- '@sveltejs/adapter-node@5.2.5(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))':
+ '@sveltejs/adapter-node@5.2.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))':
dependencies:
'@rollup/plugin-commonjs': 28.0.0(rollup@4.24.0)
'@rollup/plugin-json': 6.1.0(rollup@4.24.0)
'@rollup/plugin-node-resolve': 15.3.0(rollup@4.24.0)
- '@sveltejs/kit': 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/kit': 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
rollup: 4.24.0
- '@sveltejs/adapter-vercel@5.4.4(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))':
+ '@sveltejs/adapter-vercel@5.4.5(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))':
dependencies:
- '@sveltejs/kit': 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/kit': 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
'@vercel/nft': 0.27.4
esbuild: 0.21.5
transitivePeerDependencies:
- encoding
- supports-color
- '@sveltejs/enhanced-img@0.3.8(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))':
+ '@sveltejs/enhanced-img@0.3.8(rollup@4.24.0)(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))':
dependencies:
magic-string: 0.30.11
svelte: 5.0.0-next.175
svelte-parse-markup: 0.1.5(svelte@5.0.0-next.175)
- vite: 5.4.8(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
vite-imagetools: 7.0.4(rollup@4.24.0)
transitivePeerDependencies:
- rollup
- '@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))':
+ '@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
'@types/cookie': 0.6.0
- cookie: 0.7.2
+ cookie: 0.6.0
devalue: 5.1.1
esm-env: 1.0.0
import-meta-resolve: 4.1.0
@@ -6523,27 +6583,27 @@ snapshots:
sirv: 2.0.4
svelte: 5.0.0-next.175
tiny-glob: 0.2.9
- vite: 5.4.8(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
- '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))':
+ '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))':
dependencies:
- '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/vite-plugin-svelte': 4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
debug: 4.3.7
svelte: 5.0.0-next.175
- vite: 5.4.8(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
transitivePeerDependencies:
- supports-color
- '@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))':
+ '@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))':
dependencies:
- '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
debug: 4.3.7
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.11
svelte: 5.0.0-next.175
- vite: 5.4.8(@types/node@20.16.10)
- vitefu: 1.0.2(vite@5.4.8(@types/node@20.16.10))
+ vite: 5.4.8(@types/node@20.16.11)
+ vitefu: 1.0.2(vite@5.4.8(@types/node@20.16.11))
transitivePeerDependencies:
- supports-color
@@ -6578,19 +6638,19 @@ snapshots:
'@types/json-schema@7.0.15':
optional: true
- '@types/node@20.16.10':
+ '@types/node@20.16.11':
dependencies:
undici-types: 6.19.8
'@types/pg@8.11.10':
dependencies:
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
pg-protocol: 1.7.0
pg-types: 4.0.2
'@types/pg@8.11.6':
dependencies:
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
pg-protocol: 1.7.0
pg-types: 4.0.2
@@ -6598,7 +6658,7 @@ snapshots:
'@types/qrcode@1.5.5':
dependencies:
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
'@types/resolve@1.20.2': {}
@@ -6702,6 +6762,11 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
+ '@unhead/schema@1.11.7':
+ dependencies:
+ hookable: 5.5.3
+ zhead: 2.2.4
+
'@vercel/nft@0.27.4':
dependencies:
'@mapbox/node-pre-gyp': 1.0.11
@@ -6943,7 +7008,7 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- bullmq@5.16.0:
+ bullmq@5.17.1:
dependencies:
cron-parser: 4.9.0
ioredis: 5.4.1
@@ -7084,8 +7149,6 @@ snapshots:
cookie@0.6.0: {}
- cookie@0.7.2: {}
-
core-js@3.38.1: {}
create-require@1.1.1: {}
@@ -7404,7 +7467,7 @@ snapshots:
dependencies:
eslint: 8.57.1
- eslint-plugin-svelte@2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)):
+ eslint-plugin-svelte@2.36.0-next.13(eslint@8.57.1)(svelte@5.0.0-next.175)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)):
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1)
'@jridgewell/sourcemap-codec': 1.5.0
@@ -7414,7 +7477,7 @@ snapshots:
esutils: 2.0.3
known-css-properties: 0.30.0
postcss: 8.4.47
- postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
postcss-safe-parser: 6.0.0(postcss@8.4.47)
postcss-selector-parser: 6.1.2
semver: 7.6.3
@@ -7650,11 +7713,11 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
- formsnap@1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)):
+ formsnap@1.0.1(svelte@5.0.0-next.175)(sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)):
dependencies:
nanoid: 5.0.7
svelte: 5.0.0-next.175
- sveltekit-superforms: 2.19.1(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)
+ sveltekit-superforms: 2.19.1(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175)
forwarded@0.2.0: {}
@@ -7797,8 +7860,17 @@ snapshots:
dependencies:
hono: 4.6.3
+ hono-zod-openapi@0.2.0(hono@4.6.3)(zod@3.23.8):
+ dependencies:
+ '@hono/zod-validator': 0.4.1(hono@4.6.3)(zod@3.23.8)
+ hono: 4.6.3
+ zod: 3.23.8
+ zod-openapi: 3.1.1(zod@3.23.8)
+
hono@4.6.3: {}
+ hookable@5.5.3: {}
+
html-entities@2.5.2: {}
http-errors@2.0.0:
@@ -8531,21 +8603,21 @@ snapshots:
'@csstools/utilities': 1.0.0(postcss@8.4.47)
postcss: 8.4.47
- postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)):
+ postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)):
dependencies:
lilconfig: 2.1.0
yaml: 1.10.2
optionalDependencies:
postcss: 8.4.47
- ts-node: 10.9.2(@types/node@20.16.10)(typescript@5.6.2)
+ ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.2)
- postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)):
+ postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)):
dependencies:
lilconfig: 3.1.2
yaml: 2.5.1
optionalDependencies:
postcss: 8.4.47
- ts-node: 10.9.2(@types/node@20.16.10)(typescript@5.6.2)
+ ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.2)
postcss-load-config@5.1.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1):
dependencies:
@@ -9220,14 +9292,14 @@ snapshots:
magic-string: 0.30.11
zimmerframe: 1.1.2
- sveltekit-flash-message@2.4.4(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175):
+ sveltekit-flash-message@2.4.4(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175):
dependencies:
- '@sveltejs/kit': 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/kit': 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
svelte: 5.0.0-next.175
- sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175):
+ sveltekit-superforms@2.19.1(@sveltejs/kit@2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(@types/json-schema@7.0.15)(svelte@5.0.0-next.175):
dependencies:
- '@sveltejs/kit': 2.6.2(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.10))
+ '@sveltejs/kit': 2.6.3(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11)))(svelte@5.0.0-next.175)(vite@5.4.8(@types/node@20.16.11))
devalue: 5.1.1
just-clone: 6.2.0
memoize-weak: 1.0.2
@@ -9255,16 +9327,16 @@ snapshots:
tailwind-merge@2.5.3: {}
- tailwind-variants@0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))):
+ tailwind-variants@0.2.1(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))):
dependencies:
tailwind-merge: 2.5.3
- tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
- tailwindcss-animate@1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))):
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))):
dependencies:
- tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ tailwindcss: 3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
- tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2)):
+ tailwindcss@3.4.13(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -9283,7 +9355,7 @@ snapshots:
postcss: 8.4.47
postcss-import: 15.1.0(postcss@8.4.47)
postcss-js: 4.0.1(postcss@8.4.47)
- postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2))
+ postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))
postcss-nested: 6.2.0(postcss@8.4.47)
postcss-selector-parser: 6.1.2
resolve: 1.22.8
@@ -9352,14 +9424,14 @@ snapshots:
ts-interface-checker@0.1.13: {}
- ts-node@10.9.2(@types/node@20.16.10)(typescript@5.6.2):
+ ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
acorn: 8.12.1
acorn-walk: 8.3.4
arg: 4.1.3
@@ -9454,13 +9526,13 @@ snapshots:
transitivePeerDependencies:
- rollup
- vite-node@1.6.0(@types/node@20.16.10):
+ vite-node@1.6.0(@types/node@20.16.11):
dependencies:
cac: 6.7.14
debug: 4.3.7
pathe: 1.1.2
picocolors: 1.1.0
- vite: 5.4.8(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
transitivePeerDependencies:
- '@types/node'
- less
@@ -9472,20 +9544,20 @@ snapshots:
- supports-color
- terser
- vite@5.4.8(@types/node@20.16.10):
+ vite@5.4.8(@types/node@20.16.11):
dependencies:
esbuild: 0.21.5
postcss: 8.4.47
rollup: 4.24.0
optionalDependencies:
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
fsevents: 2.3.3
- vitefu@1.0.2(vite@5.4.8(@types/node@20.16.10)):
+ vitefu@1.0.2(vite@5.4.8(@types/node@20.16.11)):
optionalDependencies:
- vite: 5.4.8(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
- vitest@1.6.0(@types/node@20.16.10):
+ vitest@1.6.0(@types/node@20.16.11):
dependencies:
'@vitest/expect': 1.6.0
'@vitest/runner': 1.6.0
@@ -9504,11 +9576,11 @@ snapshots:
strip-literal: 2.1.0
tinybench: 2.9.0
tinypool: 0.8.4
- vite: 5.4.8(@types/node@20.16.10)
- vite-node: 1.6.0(@types/node@20.16.10)
+ vite: 5.4.8(@types/node@20.16.11)
+ vite-node: 1.6.0(@types/node@20.16.11)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 20.16.10
+ '@types/node': 20.16.11
transitivePeerDependencies:
- less
- lightningcss
@@ -9608,8 +9680,14 @@ snapshots:
type-fest: 2.19.0
optional: true
+ zhead@2.2.4: {}
+
zimmerframe@1.1.2: {}
+ zod-openapi@3.1.1(zod@3.23.8):
+ dependencies:
+ zod: 3.23.8
+
zod-to-json-schema@3.23.3(zod@3.23.8):
dependencies:
zod: 3.23.8
diff --git a/src/lib/components/PlausibleAnalytics.svelte b/src/lib/components/PlausibleAnalytics.svelte
deleted file mode 100644
index ae64c61..0000000
--- a/src/lib/components/PlausibleAnalytics.svelte
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/src/lib/server/api/common/config.ts b/src/lib/server/api/common/config.ts
index 4c8b15b..083e0b6 100644
--- a/src/lib/server/api/common/config.ts
+++ b/src/lib/server/api/common/config.ts
@@ -1,8 +1,8 @@
-import env from './env'
-import type { Config } from './types/config'
+import env from './env';
+import type { Config } from './types/config';
export const config: Config = {
- isProduction: process.env.NODE_ENV === 'production',
+ isProduction: env.NODE_ENV === 'production',
domain: env.DOMAIN,
api: {
origin: env.ORIGIN,
@@ -19,4 +19,4 @@ export const config: Config = {
ssl: false, // env.DATABASE_HOST !== 'localhost',
max: env.DB_MIGRATING || env.DB_SEEDING ? 1 : undefined,
},
-}
+};
diff --git a/src/lib/server/api/common/create-app.ts b/src/lib/server/api/common/create-app.ts
new file mode 100644
index 0000000..8df3077
--- /dev/null
+++ b/src/lib/server/api/common/create-app.ts
@@ -0,0 +1,37 @@
+import env from '$lib/server/api/common/env';
+import type { AppBindings } from '$lib/server/api/common/types/hono';
+import { validateAuthSession, verifyOrigin } from '$lib/server/api/middleware/auth.middleware';
+import { pinoLogger } from '$lib/server/api/middleware/pino-logger.middleware';
+import { Hono } from 'hono';
+import { cors } from 'hono/cors';
+import { notFound, onError, serveEmojiFavicon } from 'stoker/middlewares';
+
+export function createRouter() {
+ return new Hono({
+ strict: false,
+ }).basePath('/api');
+}
+
+export default function createApp() {
+ const app = createRouter();
+
+ app.use(verifyOrigin).use(validateAuthSession);
+ app.use(serveEmojiFavicon('📝'));
+ app.use(pinoLogger());
+
+ app.notFound(notFound);
+ app.onError(onError);
+
+ app.use(
+ '/*',
+ cors({
+ origin: [env.ORIGIN],
+
+ allowMethods: ['POST'],
+ allowHeaders: ['Content-Type'],
+ // credentials: true, // If you need to send cookies or HTTP authentication
+ }),
+ );
+
+ return app;
+}
diff --git a/src/lib/server/api/common/env.ts b/src/lib/server/api/common/env.ts
index ea4b523..ad99f4d 100644
--- a/src/lib/server/api/common/env.ts
+++ b/src/lib/server/api/common/env.ts
@@ -1,13 +1,15 @@
-import { config } from 'dotenv'
-import { expand } from 'dotenv-expand'
-import { ZodError, z } from 'zod'
+import { config } from 'dotenv';
+import { expand } from 'dotenv-expand';
+import { type ZodError, z } from 'zod';
+
+expand(config());
const stringBoolean = z.coerce
.string()
.transform((val) => {
- return val === 'true'
+ return val === 'true';
})
- .default('false')
+ .default('false');
const EnvSchema = z.object({
DATABASE_USER: z.string(),
@@ -22,34 +24,29 @@ const EnvSchema = z.object({
GITHUB_CLIENT_SECRET: z.string(),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_CLIENT_SECRET: z.string(),
+ LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),
NODE_ENV: z.string().default('development'),
ORIGIN: z.string(),
PUBLIC_SITE_NAME: z.string(),
PUBLIC_SITE_URL: z.string(),
- PUBLIC_UMAMI_DO_NOT_TRACK: z.string(),
+ PUBLIC_UMAMI_DO_NOT_TRACK: z.string().default('true'),
PUBLIC_UMAMI_ID: z.string(),
PUBLIC_UMAMI_URL: z.string(),
REDIS_URL: z.string(),
TWO_FACTOR_TIMEOUT: z.coerce.number().default(300000),
-})
+});
-export type EnvSchema = z.infer
+export type env = z.infer;
-expand(config())
+let env: env;
try {
- EnvSchema.parse(process.env)
-} catch (error) {
- if (error instanceof ZodError) {
- let message = 'Missing required values in .env:\n'
- for (const issue of error.issues) {
- message += `${issue.path[0]}\n`
- }
- const e = new Error(message)
- e.stack = ''
- throw e
- }
- console.error(error)
+ env = EnvSchema.parse(process.env);
+} catch (e) {
+ const error = e as ZodError;
+ console.error('❌ Missing required values in .env:\n');
+ console.error(error.flatten().fieldErrors);
+ process.exit(1);
}
-export default EnvSchema.parse(process.env)
+export default env;
diff --git a/src/lib/server/api/common/types/hono.ts b/src/lib/server/api/common/types/hono.ts
index b0fcae1..0974d5b 100644
--- a/src/lib/server/api/common/types/hono.ts
+++ b/src/lib/server/api/common/types/hono.ts
@@ -1,14 +1,29 @@
+import type { PinoLogger } from 'hono-pino';
import type { Promisify, RateLimitInfo } from 'hono-rate-limiter';
import type { Session, User } from 'lucia';
-export type HonoTypes = {
+export type AppBindings = {
Variables: {
+ logger: PinoLogger;
session: Session | null;
user: User | null;
rateLimit: RateLimitInfo;
- rateLimitStore: {
- getKey?: (key: string) => Promisify;
- resetKey: (key: string) => Promisify;
- };
+ rateLimitStore: {
+ getKey?: (key: string) => Promisify;
+ resetKey: (key: string) => Promisify;
+ };
+ };
+};
+
+export type HonoTypes = {
+ Variables: {
+ logger: PinoLogger;
+ session: Session | null;
+ user: User | null;
+ rateLimit: RateLimitInfo;
+ rateLimitStore: {
+ getKey?: (key: string) => Promisify;
+ resetKey: (key: string) => Promisify;
+ };
};
};
diff --git a/src/lib/server/api/configure-open-api.ts b/src/lib/server/api/configure-open-api.ts
new file mode 100644
index 0000000..ae692f3
--- /dev/null
+++ b/src/lib/server/api/configure-open-api.ts
@@ -0,0 +1,42 @@
+// // import type { AppOpenAPI } from '$lib/server/api/common/types/hono';
+// import { apiReference } from '@scalar/hono-api-reference';
+// import { Hono } from 'hono';
+//
+// import type { AppBindings } from '$lib/server/api/common/types/hono';
+// import { createOpenApiDocument } from 'hono-zod-openapi';
+// import packageJSON from '../../../../package.json';
+//
+// // export default function configureOpenAPI(app: AppOpenAPI) {
+// // app.doc('/doc', {
+// // openapi: '3.0.0',
+// // info: {
+// // title: 'Bored Game API',
+// // description: 'Bored Game API',
+// // version: packageJSON.version,
+// // },
+// // });
+// //
+// // app.get(
+// // '/reference',
+// // apiReference({
+// // theme: 'kepler',
+// // layout: 'classic',
+// // defaultHttpClient: {
+// // targetKey: 'javascript',
+// // clientKey: 'fetch',
+// // },
+// // spec: {
+// // url: '/api/doc',
+// // },
+// // }),
+// // );
+// // }
+//
+// export default function configureOpenAPI(app: Hono) {
+// createOpenApiDocument(app, {
+// info: {
+// title: 'Example API',
+// version: '1.0.0',
+// },
+// });
+// }
diff --git a/src/lib/server/api/controllers/iam.controller.ts b/src/lib/server/api/controllers/iam.controller.ts
index 0d2010c..32f650a 100644
--- a/src/lib/server/api/controllers/iam.controller.ts
+++ b/src/lib/server/api/controllers/iam.controller.ts
@@ -1,17 +1,17 @@
-import { StatusCodes } from '$lib/constants/status-codes'
-import { Controller } from '$lib/server/api/common/types/controller'
-import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto'
-import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto'
-import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto'
-import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto'
-import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware'
-import { IamService } from '$lib/server/api/services/iam.service'
-import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service'
-import { LuciaService } from '$lib/server/api/services/lucia.service'
-import { zValidator } from '@hono/zod-validator'
-import { setCookie } from 'hono/cookie'
-import { inject, injectable } from 'tsyringe'
-import { requireAuth } from '../middleware/require-auth.middleware'
+import { StatusCodes } from '$lib/constants/status-codes';
+import { Controller } from '$lib/server/api/common/types/controller';
+import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto';
+import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto';
+import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto';
+import { verifyPasswordDto } from '$lib/server/api/dtos/verify-password.dto';
+import { limiter } from '$lib/server/api/middleware/rate-limiter.middleware';
+import { IamService } from '$lib/server/api/services/iam.service';
+import { LoginRequestsService } from '$lib/server/api/services/loginrequest.service';
+import { LuciaService } from '$lib/server/api/services/lucia.service';
+import { zValidator } from '@hono/zod-validator';
+import { setCookie } from 'hono/cookie';
+import { inject, injectable } from 'tsyringe';
+import { requireAuth } from '../middleware/require-auth.middleware';
@injectable()
export class IamController extends Controller {
@@ -20,45 +20,47 @@ export class IamController extends Controller {
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService,
@inject(LuciaService) private luciaService: LuciaService,
) {
- super()
+ super();
}
routes() {
+ const tags = ['IAM'];
+
return this.controller
.get('/', requireAuth, async (c) => {
- const user = c.var.user
- return c.json({ user })
+ const user = c.var.user;
+ return c.json({ user });
})
.put('/update/profile', requireAuth, zValidator('json', updateProfileDto), limiter({ limit: 30, minutes: 60 }), async (c) => {
- const user = c.var.user
- const { firstName, lastName, username } = c.req.valid('json')
- const updatedUser = await this.iamService.updateProfile(user.id, { firstName, lastName, username })
+ const user = c.var.user;
+ const { firstName, lastName, username } = c.req.valid('json');
+ const updatedUser = await this.iamService.updateProfile(user.id, { firstName, lastName, username });
if (!updatedUser) {
- return c.json('Username already in use', StatusCodes.BAD_REQUEST)
+ return c.json('Username already in use', StatusCodes.BAD_REQUEST);
}
- return c.json({ user: updatedUser }, StatusCodes.OK)
+ return c.json({ user: updatedUser }, StatusCodes.OK);
})
.post('/verify/password', requireAuth, zValidator('json', verifyPasswordDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
- const user = c.var.user
- const { password } = c.req.valid('json')
- const passwordVerified = await this.iamService.verifyPassword(user.id, { password })
+ const user = c.var.user;
+ const { password } = c.req.valid('json');
+ const passwordVerified = await this.iamService.verifyPassword(user.id, { password });
if (!passwordVerified) {
- console.log('Incorrect password')
- return c.json('Incorrect password', StatusCodes.BAD_REQUEST)
+ console.log('Incorrect password');
+ return c.json('Incorrect password', StatusCodes.BAD_REQUEST);
}
- return c.json({}, StatusCodes.OK)
+ return c.json({}, StatusCodes.OK);
})
.put('/update/password', requireAuth, zValidator('json', changePasswordDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
- const user = c.var.user
- const { password, confirm_password } = c.req.valid('json')
+ const user = c.var.user;
+ const { password, confirm_password } = c.req.valid('json');
if (password !== confirm_password) {
- return c.json('Passwords do not match', StatusCodes.BAD_REQUEST)
+ return c.json('Passwords do not match', StatusCodes.BAD_REQUEST);
}
try {
- await this.iamService.updatePassword(user.id, { password, confirm_password })
- await this.luciaService.lucia.invalidateUserSessions(user.id)
- await this.loginRequestService.createUserSession(user.id, c.req, undefined)
- const sessionCookie = this.luciaService.lucia.createBlankSessionCookie()
+ await this.iamService.updatePassword(user.id, { password, confirm_password });
+ await this.luciaService.lucia.invalidateUserSessions(user.id);
+ await this.loginRequestService.createUserSession(user.id, c.req, undefined);
+ const sessionCookie = this.luciaService.lucia.createBlankSessionCookie();
setCookie(c, sessionCookie.name, sessionCookie.value, {
path: sessionCookie.attributes.path,
maxAge: sessionCookie.attributes.maxAge,
@@ -67,26 +69,26 @@ export class IamController extends Controller {
secure: sessionCookie.attributes.secure,
httpOnly: sessionCookie.attributes.httpOnly,
expires: sessionCookie.attributes.expires,
- })
- return c.json({ status: 'success' })
+ });
+ return c.json({ status: 'success' });
} catch (error) {
- console.error('Error updating password', error)
- return c.json('Error updating password', StatusCodes.BAD_REQUEST)
+ console.error('Error updating password', error);
+ return c.json('Error updating password', StatusCodes.BAD_REQUEST);
}
})
.post('/update/email', requireAuth, zValidator('json', updateEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
- const user = c.var.user
- const { email } = c.req.valid('json')
- const updatedUser = await this.iamService.updateEmail(user.id, { email })
+ const user = c.var.user;
+ const { email } = c.req.valid('json');
+ const updatedUser = await this.iamService.updateEmail(user.id, { email });
if (!updatedUser) {
- return c.json('Email already in use', StatusCodes.BAD_REQUEST)
+ return c.json('Email already in use', StatusCodes.BAD_REQUEST);
}
- return c.json({ user: updatedUser }, StatusCodes.OK)
+ return c.json({ user: updatedUser }, StatusCodes.OK);
})
.post('/logout', requireAuth, async (c) => {
- const sessionId = c.var.session.id
- await this.iamService.logout(sessionId)
- const sessionCookie = this.luciaService.lucia.createBlankSessionCookie()
+ const sessionId = c.var.session.id;
+ await this.iamService.logout(sessionId);
+ const sessionCookie = this.luciaService.lucia.createBlankSessionCookie();
setCookie(c, sessionCookie.name, sessionCookie.value, {
path: sessionCookie.attributes.path,
maxAge: sessionCookie.attributes.maxAge,
@@ -95,8 +97,8 @@ export class IamController extends Controller {
secure: sessionCookie.attributes.secure,
httpOnly: sessionCookie.attributes.httpOnly,
expires: sessionCookie.attributes.expires,
- })
- return c.json({ status: 'success' })
- })
+ });
+ return c.json({ status: 'success' });
+ });
}
}
diff --git a/src/lib/server/api/controllers/user.controller.ts b/src/lib/server/api/controllers/user.controller.ts
index c05cd2e..46350e3 100644
--- a/src/lib/server/api/controllers/user.controller.ts
+++ b/src/lib/server/api/controllers/user.controller.ts
@@ -1,30 +1,30 @@
-import 'reflect-metadata'
-import { Controller } from '$lib/server/api/common/types/controller'
-import { UsersService } from '$lib/server/api/services/users.service'
-import { inject, injectable } from 'tsyringe'
-import { requireAuth } from '../middleware/require-auth.middleware'
+import 'reflect-metadata';
+import { Controller } from '$lib/server/api/common/types/controller';
+import { UsersService } from '$lib/server/api/services/users.service';
+import { inject, injectable } from 'tsyringe';
+import { requireAuth } from '../middleware/require-auth.middleware';
@injectable()
export class UserController extends Controller {
constructor(@inject(UsersService) private readonly usersService: UsersService) {
- super()
+ super();
}
routes() {
return this.controller
.get('/', async (c) => {
- const user = c.var.user
- return c.json({ user })
+ const user = c.var.user;
+ return c.json({ user });
})
.get('/:id', requireAuth, async (c) => {
- const id = c.req.param('id')
- const user = await this.usersService.findOneById(id)
- return c.json({ user })
+ const id = c.req.param('id');
+ const user = await this.usersService.findOneById(id);
+ return c.json({ user });
})
.get('/username/:userName', requireAuth, async (c) => {
- const userName = c.req.param('userName')
- const user = await this.usersService.findOneByUsername(userName)
- return c.json({ user })
- })
+ const userName = c.req.param('userName');
+ const user = await this.usersService.findOneByUsername(userName);
+ return c.json({ user });
+ });
}
}
diff --git a/src/lib/server/api/databases/seeds/users.ts b/src/lib/server/api/databases/seeds/users.ts
index ee039f2..b847cc2 100644
--- a/src/lib/server/api/databases/seeds/users.ts
+++ b/src/lib/server/api/databases/seeds/users.ts
@@ -1,20 +1,19 @@
-import { eq } from 'drizzle-orm'
-import type { db } from '../../packages/drizzle'
-import { HashingService } from '../../services/hashing.service'
-import * as schema from '../tables'
-import users from './data/users.json'
+import { eq } from 'drizzle-orm';
+import type { db } from '../../packages/drizzle';
+import { HashingService } from '../../services/hashing.service';
+import * as schema from '../tables';
+import users from './data/users.json';
type JsonRole = {
- name: string
- primary: boolean
-}
+ name: string;
+ primary: boolean;
+};
export default async function seed(db: db) {
- const hashingService = new HashingService()
- const adminRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'admin'))
- const userRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'user'))
+ const hashingService = new HashingService();
+ const adminRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'admin'));
+ const userRole = await db.select().from(schema.rolesTable).where(eq(schema.rolesTable.name, 'user'));
- console.log('Admin Role: ', adminRole)
const adminUser = await db
.insert(schema.usersTable)
.values({
@@ -25,19 +24,17 @@ export default async function seed(db: db) {
verified: true,
})
.returning()
- .onConflictDoNothing()
-
- console.log('Admin user created.', adminUser)
+ .onConflictDoNothing();
await db.insert(schema.credentialsTable).values({
user_id: adminUser[0].id,
type: schema.CredentialsType.PASSWORD,
secret_data: await hashingService.hash(`${process.env.ADMIN_PASSWORD}`),
- })
+ });
- await db.insert(schema.collections).values({ user_id: adminUser[0].id }).onConflictDoNothing()
+ await db.insert(schema.collections).values({ user_id: adminUser[0].id }).onConflictDoNothing();
- await db.insert(schema.wishlistsTable).values({ user_id: adminUser[0].id }).onConflictDoNothing()
+ await db.insert(schema.wishlistsTable).values({ user_id: adminUser[0].id }).onConflictDoNothing();
await db
.insert(schema.user_roles)
@@ -46,9 +43,7 @@ export default async function seed(db: db) {
role_id: adminRole[0].id,
primary: true,
})
- .onConflictDoNothing()
-
- console.log('Admin user given admin role.')
+ .onConflictDoNothing();
await db
.insert(schema.user_roles)
@@ -57,10 +52,8 @@ export default async function seed(db: db) {
role_id: userRole[0].id,
primary: false,
})
- .onConflictDoNothing()
+ .onConflictDoNothing();
- console.log('Admin user given user role.')
- const hasingService = new HashingService()
await Promise.all(
users.map(async (user) => {
const [insertedUser] = await db
@@ -68,29 +61,29 @@ export default async function seed(db: db) {
.values({
...user,
})
- .returning()
+ .returning();
await db.insert(schema.credentialsTable).values({
user_id: insertedUser?.id,
type: schema.CredentialsType.PASSWORD,
- secret_data: await hasingService.hash(user.password),
- })
- await db.insert(schema.collections).values({ user_id: insertedUser?.id })
- await db.insert(schema.wishlistsTable).values({ user_id: insertedUser?.id })
+ secret_data: await hashingService.hash(user.password),
+ });
+ await db.insert(schema.collections).values({ user_id: insertedUser?.id });
+ await db.insert(schema.wishlistsTable).values({ user_id: insertedUser?.id });
await Promise.all(
user.roles.map(async (role: JsonRole) => {
const foundRole = await db.query.rolesTable.findFirst({
where: eq(schema.rolesTable.name, role.name),
- })
+ });
if (!foundRole) {
- throw new Error('Role not found')
+ throw new Error('Role not found');
}
await db.insert(schema.user_roles).values({
user_id: insertedUser?.id,
role_id: foundRole?.id,
primary: role?.primary,
- })
+ });
}),
- )
+ );
}),
- )
+ );
}
diff --git a/src/lib/server/api/index.ts b/src/lib/server/api/index.ts
index a057c76..28095bf 100644
--- a/src/lib/server/api/index.ts
+++ b/src/lib/server/api/index.ts
@@ -1,53 +1,21 @@
-import 'reflect-metadata'
-import { CollectionController } from '$lib/server/api/controllers/collection.controller'
-import { MfaController } from '$lib/server/api/controllers/mfa.controller'
-import { OAuthController } from '$lib/server/api/controllers/oauth.controller'
-import { SignupController } from '$lib/server/api/controllers/signup.controller'
-import { UserController } from '$lib/server/api/controllers/user.controller'
-import { WishlistController } from '$lib/server/api/controllers/wishlist.controller'
-import { AuthCleanupJobs } from '$lib/server/api/jobs/auth-cleanup.job'
-import { OpenAPIHono } from '@hono/zod-openapi'
-import type { PinoLogger } from 'hono-pino'
-import { hc } from 'hono/client'
-import { cors } from 'hono/cors'
-import { notFound, onError } from 'stoker/middlewares';
-import { container } from 'tsyringe'
-import { config } from './common/config'
-import { IamController } from './controllers/iam.controller'
-import { LoginController } from './controllers/login.controller'
-import { validateAuthSession, verifyOrigin } from './middleware/auth.middleware'
-import { pinoLogger } from './middleware/pino-logger.middleware'
+import 'reflect-metadata';
+import createApp from '$lib/server/api/common/create-app';
+import { CollectionController } from '$lib/server/api/controllers/collection.controller';
+import { MfaController } from '$lib/server/api/controllers/mfa.controller';
+import { OAuthController } from '$lib/server/api/controllers/oauth.controller';
+import { SignupController } from '$lib/server/api/controllers/signup.controller';
+import { UserController } from '$lib/server/api/controllers/user.controller';
+import { WishlistController } from '$lib/server/api/controllers/wishlist.controller';
+import { AuthCleanupJobs } from '$lib/server/api/jobs/auth-cleanup.job';
+import { hc } from 'hono/client';
+import { container } from 'tsyringe';
+import { config } from './common/config';
+import { IamController } from './controllers/iam.controller';
+import { LoginController } from './controllers/login.controller';
-type AppBindings = {
- Variables: {
- logger: PinoLogger
- }
-}
+export const app = createApp();
-/* -------------------------------------------------------------------------- */
-/* App */
-/* -------------------------------------------------------------------------- */
-export const app = new OpenAPIHono().basePath('/api')
-
-/* -------------------------------------------------------------------------- */
-/* Global Middlewares */
-/* -------------------------------------------------------------------------- */
-app.use(verifyOrigin).use(validateAuthSession)
-app.use(pinoLogger())
-
-app.notFound(notFound)
-app.onError(onError)
-
-app.use(
- '/*',
- cors({
- origin: ['http://localhost:5173', 'http://localhost:80', 'http://host.docker.internal:80', 'http://host.docker.internal:5173'], // Replace with your allowed domains
-
- allowMethods: ['POST'],
- allowHeaders: ['Content-Type'],
- // credentials: true, // If you need to send cookies or HTTP authentication
- }),
-)
+// configureOpenAPI(app);
/* -------------------------------------------------------------------------- */
/* Routes */
@@ -61,22 +29,17 @@ const routes = app
.route('/wishlists', container.resolve(WishlistController).routes())
.route('/collections', container.resolve(CollectionController).routes())
.route('/mfa', container.resolve(MfaController).routes())
- .get('/', (c) => c.json({ message: 'Server is healthy' }))
- .get('/error', (c) => {
- c.status(422);
- c.var.logger.info('Logged here');
- throw new Error('Test error')
- })
+ .get('/', (c) => c.json({ message: 'Server is healthy' }));
/* -------------------------------------------------------------------------- */
/* Cron Jobs */
/* -------------------------------------------------------------------------- */
-container.resolve(AuthCleanupJobs).deleteStaleEmailVerificationRequests()
-container.resolve(AuthCleanupJobs).deleteStaleLoginRequests()
+container.resolve(AuthCleanupJobs).deleteStaleEmailVerificationRequests();
+container.resolve(AuthCleanupJobs).deleteStaleLoginRequests();
/* -------------------------------------------------------------------------- */
/* Exports */
/* -------------------------------------------------------------------------- */
-export const rpc = hc(config.api.origin)
-export type ApiClient = typeof rpc
-export type ApiRoutes = typeof routes
+export const rpc = hc(config.api.origin);
+export type ApiClient = typeof rpc;
+export type ApiRoutes = typeof routes;
diff --git a/src/lib/server/api/middleware/auth.middleware.ts b/src/lib/server/api/middleware/auth.middleware.ts
index b88418b..fc4e076 100644
--- a/src/lib/server/api/middleware/auth.middleware.ts
+++ b/src/lib/server/api/middleware/auth.middleware.ts
@@ -1,41 +1,41 @@
-import { LuciaService } from '$lib/server/api/services/lucia.service'
-import type { MiddlewareHandler } from 'hono'
-import { createMiddleware } from 'hono/factory'
-import { verifyRequestOrigin } from 'oslo/request'
-import { container } from 'tsyringe'
-import type { HonoTypes } from '../common/types/hono'
+import { LuciaService } from '$lib/server/api/services/lucia.service';
+import type { MiddlewareHandler } from 'hono';
+import { createMiddleware } from 'hono/factory';
+import { verifyRequestOrigin } from 'oslo/request';
+import { container } from 'tsyringe';
+import type { AppBindings } from '../common/types/hono';
// resolve dependencies from the container
-const { lucia } = container.resolve(LuciaService)
+const { lucia } = container.resolve(LuciaService);
-export const verifyOrigin: MiddlewareHandler = createMiddleware(async (c, next) => {
+export const verifyOrigin: MiddlewareHandler = createMiddleware(async (c, next) => {
if (c.req.method === 'GET') {
- return next()
+ return next();
}
- const originHeader = c.req.header('Origin') ?? null
- const hostHeader = c.req.header('Host') ?? null
+ const originHeader = c.req.header('Origin') ?? null;
+ const hostHeader = c.req.header('Host') ?? null;
if (!originHeader || !hostHeader || !verifyRequestOrigin(originHeader, [hostHeader])) {
- return c.body(null, 403)
+ return c.body(null, 403);
}
- return next()
-})
+ return next();
+});
-export const validateAuthSession: MiddlewareHandler = createMiddleware(async (c, next) => {
- const sessionId = lucia.readSessionCookie(c.req.header('Cookie') ?? '')
+export const validateAuthSession: MiddlewareHandler = createMiddleware(async (c, next) => {
+ const sessionId = lucia.readSessionCookie(c.req.header('Cookie') ?? '');
if (!sessionId) {
- c.set('user', null)
- c.set('session', null)
- return next()
+ c.set('user', null);
+ c.set('session', null);
+ return next();
}
- const { session, user } = await lucia.validateSession(sessionId)
+ const { session, user } = await lucia.validateSession(sessionId);
if (session?.fresh) {
- c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true })
+ c.header('Set-Cookie', lucia.createSessionCookie(session.id).serialize(), { append: true });
}
if (!session) {
- c.header('Set-Cookie', lucia.createBlankSessionCookie().serialize(), { append: true })
+ c.header('Set-Cookie', lucia.createBlankSessionCookie().serialize(), { append: true });
}
- c.set('session', session)
- c.set('user', user)
- return next()
-})
+ c.set('session', session);
+ c.set('user', user);
+ return next();
+});
diff --git a/src/lib/server/api/middleware/pino-logger.middleware.ts b/src/lib/server/api/middleware/pino-logger.middleware.ts
index fc98d10..6cd74e6 100644
--- a/src/lib/server/api/middleware/pino-logger.middleware.ts
+++ b/src/lib/server/api/middleware/pino-logger.middleware.ts
@@ -1,14 +1,18 @@
+import env from '$lib/server/api/common/env';
import { logger } from 'hono-pino';
import pino from 'pino';
import pretty from 'pino-pretty';
export function pinoLogger() {
return logger({
- pino: pino({
- level: process.env.LOG_LEVEL || 'info',
- }, process.env.NODE_ENV === 'production' ? undefined : pretty()),
+ pino: pino(
+ {
+ level: env.LOG_LEVEL || 'info',
+ },
+ env.NODE_ENV === 'production' ? undefined : pretty(),
+ ),
http: {
reqId: () => crypto.randomUUID(),
- }
+ },
});
-}
\ No newline at end of file
+}
diff --git a/src/lib/server/api/middleware/rate-limiter.middleware.ts b/src/lib/server/api/middleware/rate-limiter.middleware.ts
index 97f0919..474bbe0 100644
--- a/src/lib/server/api/middleware/rate-limiter.middleware.ts
+++ b/src/lib/server/api/middleware/rate-limiter.middleware.ts
@@ -1,35 +1,35 @@
-import { rateLimiter } from 'hono-rate-limiter'
-import { RedisStore } from 'rate-limit-redis'
-import { container } from 'tsyringe'
-import type { HonoTypes } from '../common/types/hono'
-import { RedisService } from '../services/redis.service'
+import { rateLimiter } from 'hono-rate-limiter';
+import { RedisStore } from 'rate-limit-redis';
+import { container } from 'tsyringe';
+import type { AppBindings } from '../common/types/hono';
+import { RedisService } from '../services/redis.service';
// resolve dependencies from the container
-const { client } = container.resolve(RedisService)
+const { client } = container.resolve(RedisService);
export function limiter({
limit,
minutes,
key = '',
}: {
- limit: number
- minutes: number
- key?: string
+ limit: number;
+ minutes: number;
+ key?: string;
}) {
return rateLimiter({
windowMs: minutes * 60 * 1000, // every x minutes
limit, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-6', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
keyGenerator: (c) => {
- const vars = c.var as HonoTypes['Variables']
- const clientKey = vars.user?.id || c.req.header('x-forwarded-for')
- const pathKey = key || c.req.routePath
- return `${clientKey}_${pathKey}`
+ const vars = c.var as AppBindings['Variables'];
+ const clientKey = vars.user?.id || c.req.header('x-forwarded-for');
+ const pathKey = key || c.req.routePath;
+ return `${clientKey}_${pathKey}`;
}, // Method to generate custom identifiers for clients.
// Redis store configuration
store: new RedisStore({
// @ts-expect-error - Known issue: the `call` function is not present in @types/ioredis
sendCommand: (...args: string[]) => client.call(...args),
}) as any,
- })
+ });
}
diff --git a/src/lib/server/api/services/drizzle.service.ts b/src/lib/server/api/services/drizzle.service.ts
index 93be44b..29e1f8a 100644
--- a/src/lib/server/api/services/drizzle.service.ts
+++ b/src/lib/server/api/services/drizzle.service.ts
@@ -1,14 +1,15 @@
-import { type NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres'
-import pg from 'pg'
-import { type Disposable, injectable } from 'tsyringe'
-import { config } from '../common/config'
-import * as schema from '../databases/tables'
+import env from '$lib/server/api/common/env';
+import { type NodePgDatabase, drizzle } from 'drizzle-orm/node-postgres';
+import pg from 'pg';
+import { type Disposable, injectable } from 'tsyringe';
+import { config } from '../common/config';
+import * as schema from '../databases/tables';
@injectable()
export class DrizzleService implements Disposable {
- protected readonly pool: pg.Pool
- db: NodePgDatabase
- readonly schema: typeof schema = schema
+ protected readonly pool: pg.Pool;
+ db: NodePgDatabase;
+ readonly schema: typeof schema = schema;
constructor() {
const pool = new pg.Pool({
@@ -19,15 +20,15 @@ export class DrizzleService implements Disposable {
database: config.postgres.database,
ssl: config.postgres.ssl,
max: config.postgres.max,
- })
- this.pool = pool
+ });
+ this.pool = pool;
this.db = drizzle(pool, {
schema,
- logger: process.env.NODE_ENV === 'development',
- })
+ logger: env.NODE_ENV === 'development',
+ });
}
dispose(): Promise | void {
- this.pool.end()
+ this.pool.end();
}
}
diff --git a/src/lib/server/api/services/totp.service.ts b/src/lib/server/api/services/totp.service.ts
index 80480c0..6e8b7df 100644
--- a/src/lib/server/api/services/totp.service.ts
+++ b/src/lib/server/api/services/totp.service.ts
@@ -1,52 +1,52 @@
-import { CredentialsRepository } from '$lib/server/api/repositories/credentials.repository'
-import { decodeHex, encodeHexLowerCase } from '@oslojs/encoding'
-import { verifyTOTP } from '@oslojs/otp'
-import { inject, injectable } from 'tsyringe'
-import type { CredentialsType } from '../databases/tables'
+import { CredentialsRepository } from '$lib/server/api/repositories/credentials.repository';
+import { decodeHex, encodeHexLowerCase } from '@oslojs/encoding';
+import { verifyTOTP } from '@oslojs/otp';
+import { inject, injectable } from 'tsyringe';
+import type { CredentialsType } from '../databases/tables';
@injectable()
export class TotpService {
constructor(@inject(CredentialsRepository) private readonly credentialsRepository: CredentialsRepository) {}
async findOneByUserId(userId: string) {
- return this.credentialsRepository.findTOTPCredentialsByUserId(userId)
+ return this.credentialsRepository.findTOTPCredentialsByUserId(userId);
}
async findOneByUserIdOrThrow(userId: string) {
- const credential = await this.findOneByUserId(userId)
+ const credential = await this.findOneByUserId(userId);
if (!credential) {
- throw new Error('TOTP credential not found')
+ throw new Error('TOTP credential not found');
}
- return credential
+ return credential;
}
async create(userId: string) {
- const secret = new Uint8Array(20)
+ const secret = new Uint8Array(20);
try {
return await this.credentialsRepository.create({
user_id: userId,
secret_data: encodeHexLowerCase(crypto.getRandomValues(secret)),
type: 'totp',
- })
+ });
} catch (e) {
- console.error(e)
- return null
+ console.error(e);
+ return null;
}
}
async deleteOneByUserId(userId: string) {
- return this.credentialsRepository.deleteByUserId(userId)
+ return this.credentialsRepository.deleteByUserId(userId);
}
async deleteOneByUserIdAndType(userId: string, type: CredentialsType) {
- return this.credentialsRepository.deleteByUserIdAndType(userId, type)
+ return this.credentialsRepository.deleteByUserIdAndType(userId, type);
}
async verify(userId: string, code: string) {
- const credential = await this.credentialsRepository.findTOTPCredentialsByUserId(userId)
+ const credential = await this.credentialsRepository.findTOTPCredentialsByUserId(userId);
if (!credential) {
- throw new Error('TOTP credential not found')
+ throw new Error('TOTP credential not found');
}
- return verifyTOTP(decodeHex(credential.secret_data), 30, 6, code)
+ return verifyTOTP(decodeHex(credential.secret_data), 30, 6, code);
}
}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 4c22a8f..45f7968 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,23 +1,22 @@
{#if !dev}
-
{/if}