diff --git a/package.json b/package.json index 9872883..9d8fe1d 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,6 @@ "mime": "^4.0.4", "minio": "^7.1.4", "svelte-sonner": "^0.3.27", - "validator": "^13.12.0" + "zod": "^3.23.8" } } diff --git a/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql b/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql new file mode 100644 index 0000000..d46c83e --- /dev/null +++ b/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql @@ -0,0 +1,16 @@ +-- AlterTable +ALTER TABLE `User` ADD COLUMN `totp` VARCHAR(191) NULL; + +-- CreateTable +CREATE TABLE `APIKey` ( + `id` VARCHAR(191) NOT NULL, + `userId` INTEGER NOT NULL, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `expiresAt` DATETIME(3) NOT NULL, + `permissions` INTEGER NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `APIKey` ADD CONSTRAINT `APIKey_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 19ef386..7775c27 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -12,6 +12,7 @@ model User { username String @unique email String @unique password String + totp String? role Role createdAt DateTime @default(now()) lastSeen DateTime @default(now()) @@ -20,6 +21,7 @@ model User { maxUploadMB Int @default(100) uploads Upload[] sessions Session[] + apiKeys APIKey[] } model Session { @@ -58,6 +60,15 @@ model Upload { uploaded DateTime @default(now()) } +model APIKey { + id String @id + user User @relation(fields: [userId], references: [id]) + userId Int + createdAt DateTime @default(now()) + expiresAt DateTime + permissions Int +} + enum Role { ADMINISTRATOR USER diff --git a/src/hooks.server.js b/src/hooks.server.js deleted file mode 100644 index fbb3424..0000000 --- a/src/hooks.server.js +++ /dev/null @@ -1,44 +0,0 @@ -import { error, redirect } from '@sveltejs/kit'; -import { getSession } from '$lib/server/database'; -import { COOKIE } from '$lib/config'; - -const PUBLIC_RESOURCES = [ - '/', - '/api', - '/api/auth/register', - '/api/auth/login', - '/terms', - '/privacy' -]; - -/** @type {import('@sveltejs/kit').Handle} */ -export async function handle({ event, resolve }) { - const { cookies, locals } = event; - const session = await getSession(cookies.get(COOKIE) || ''); - - if (session && session.user) { - locals.user = { - id: session.user.id, - username: session.user.username, - email: session.user.email, - maxUploadMB: session.user.maxUploadMB, - role: session.user.role - }; - } else { - if (event.route.id) { - if (event.route.id.includes('(app)')) return redirect(303, '/'); - } - } - - return await resolve(event); -} - -/** @type {import('@sveltejs/kit').HandleServerError} */ -export async function handleError({ error, event, status, message }) { - console.log(error); - - return { - status, - message - }; -} diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..c879a79 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,52 @@ +import { redirect } from '@sveltejs/kit'; +import { getSession, getUserApiKey } from '$lib/server/database'; +import { COOKIE } from '$lib/config'; + +export async function handle({ event, resolve }) { + const { cookies, locals, request } = event; + + let cookie = cookies.get(COOKIE); + let bearer = request.headers.get('Authorization'); + if (bearer) bearer = bearer.replace('Bearer ', ''); + + if (cookie) { + const session = await getSession(cookie); + if (session && session.user) { + locals.user = { + id: session.user.id, + username: session.user.username, + email: session.user.email, + maxUploadMB: session.user.maxUploadMB, + role: session.user.role + }; + } + } + + if (bearer && !locals.user) { + const apiKey = await getUserApiKey(bearer); + if (apiKey && apiKey.user) { + locals.user = { + id: apiKey.user.id, + username: apiKey.user.username, + email: apiKey.user.email, + maxUploadMB: apiKey.user.maxUploadMB, + role: apiKey.user.role + }; + } + } + + if (!locals.user && event.route.id) { + if (event.route.id.includes('(app)')) return redirect(303, '/'); + } + + return await resolve(event); +} + +export async function handleError({ error, status, message }) { + console.log(error); + + return { + status, + message + }; +} diff --git a/src/lib/components/Dropdown.svelte b/src/lib/components/Dropdown.svelte index cb32b6d..676e3df 100644 --- a/src/lib/components/Dropdown.svelte +++ b/src/lib/components/Dropdown.svelte @@ -25,7 +25,7 @@ {#if visible}
diff --git a/src/lib/components/Forms/LoginForm.svelte b/src/lib/components/Forms/LoginForm.svelte index 3c9454e..e5126f4 100644 --- a/src/lib/components/Forms/LoginForm.svelte +++ b/src/lib/components/Forms/LoginForm.svelte @@ -1,4 +1,5 @@ - - + + {#if type === 'username'} -
-
+
+
- -
- -
{:else if type === 'email'} -
-
+
+
- -
- -
{:else if type === 'password'} -
-
+
+
- -
- -
{/if} diff --git a/src/lib/components/Inputs/Link.svelte b/src/lib/components/Inputs/Link.svelte index ed9391f..8c535f9 100644 --- a/src/lib/components/Inputs/Link.svelte +++ b/src/lib/components/Inputs/Link.svelte @@ -1,15 +1,17 @@ {#if style === 'link'} - + {:else if style === 'button'} diff --git a/src/lib/components/ListedAPIKey.svelte b/src/lib/components/ListedAPIKey.svelte new file mode 100644 index 0000000..1711adc --- /dev/null +++ b/src/lib/components/ListedAPIKey.svelte @@ -0,0 +1,24 @@ + + + + + {key.id} + + + + + diff --git a/src/lib/config.ts b/src/lib/config.ts index 5f1add6..27c20c2 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -12,3 +12,25 @@ export const MAIL_WHITELIST = [ 'pm.me', 'proton.me' ]; + +export const DOMAINS = [ + 'cdn.cirroskais.xyz', + 'cdn.madhouselabs.net', + 'snep.lol', + 'i.chadthundercock.com', + 'doing-ya.mom', + '*.is-gay.zip' +]; + +export const DEV_DOMAINS = ['cdn.dev.madhouselabs.net']; + +export const API_KEY_PERMISSIONS = { + CREATE_UPLOADS: 1 << 0, + READ_UPLOADS: 1 << 1, + UPDATE_UPLOADS: 1 << 2, + DELETE_UPLOADS: 1 << 3, + + READ_ACCOUNT: 1 << 4, + UPDATE_ACCOUNT_ACCOUNT_SETTINGS: 1 << 5, // allows for updating username & email. bad idea? probably. + UPDATE_ACCOUNT_EMBED_SETTINGS: 1 << 6 +}; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 7788f04..181e1c7 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -1,7 +1,18 @@ import { COOKIE } from '$lib/config'; import type { Cookies } from '@sveltejs/kit'; -import { getSession } from './database'; -import type { User, UserSettings } from '@prisma/client'; +import { getSession, getUserApiKey } from './database'; +import type { Role, UserSettings } from '@prisma/client'; + +interface User { + id: number; + username: string; + email: string; + password: string; + role: Role; + createdAt: Date; + lastSeen: Date; + maxUploadMB: number; +} interface UserAndMaybeSettings extends User { settings: UserSettings | null; @@ -14,7 +25,10 @@ export async function authenticate(request: Request, cookies: Cookies) { let user: UserAndMaybeSettings | false = false; if (bearer && !cookie) { - return false; + const key = await getUserApiKey(bearer); + if (key) { + user = key.user; + } } if (cookie && !bearer) { diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts index fdbdde6..f61e24f 100644 --- a/src/lib/server/database.ts +++ b/src/lib/server/database.ts @@ -138,3 +138,62 @@ export async function getSettings(id: number) { } }); } + +export async function getUserApiKeys(userId: number) { + if (!userId) return false; + + return await prisma.aPIKey.findMany({ + where: { + userId: userId + } + }); +} + +export async function createUserApiKey(userId: number, permissions: number, expiresAt: Date) { + if (!userId) return false; + + return await prisma.aPIKey.create({ + data: { + id: randomBytes(42).toString('base64url'), + userId, + permissions, + expiresAt + } + }); +} + +export async function deleteUserApiKey(userId: number, id: string) { + if (!userId || !id) return false; + + return await prisma.aPIKey.delete({ + where: { + userId, + id + } + }); +} + +export async function getUserApiKey(id: string) { + if (!id) return false; + + return await prisma.aPIKey.findFirst({ + where: { + id + }, + include: { + user: { + select: { + id: true, + username: true, + email: true, + password: true, + role: true, + createdAt: true, + lastSeen: true, + maxUploadMB: true, + settings: true + } + } + } + }); +} diff --git a/src/lib/server/ratelimit.js b/src/lib/server/ratelimit.ts similarity index 94% rename from src/lib/server/ratelimit.js rename to src/lib/server/ratelimit.ts index 5b4ffa9..5de8fa8 100644 --- a/src/lib/server/ratelimit.js +++ b/src/lib/server/ratelimit.ts @@ -16,7 +16,7 @@ const ApiKeyLimits = { setInterval(function resetMinute() {}, 1000 * 60); setInterval(function resetHour() {}, 1000 * 60 * 60); -function hash(input) { +function hash(input: string) { createHash('sha256').update(input).digest('hex'); } diff --git a/src/lib/server/validator.js b/src/lib/server/validator.js deleted file mode 100644 index 62784b6..0000000 --- a/src/lib/server/validator.js +++ /dev/null @@ -1,8 +0,0 @@ -import validator from 'validator'; -import { MAIL_WHITELIST } from '$lib/config'; - -// https://github.com/validatorjs/validator.js?tab=readme-ov-file#validators - -export function email(input) { - return validator.isEmail(input, { host_whitelist: MAIL_WHITELIST }); -} diff --git a/src/lib/server/validator.ts b/src/lib/server/validator.ts new file mode 100644 index 0000000..cd0aea7 --- /dev/null +++ b/src/lib/server/validator.ts @@ -0,0 +1,42 @@ +import { z } from 'zod'; +import { findUser } from './database'; + +const INTERNAL_email = z.string().email('Invalid email address.'); + +export const email = INTERNAL_email.parse; + +export const emailAndNotUsed = INTERNAL_email.refine(async (_) => { + return !Boolean(await findUser({ email: _ })); +}, 'Email is already being used.').parseAsync; + +const INTERNAL_username = z + .string() + .min(3, 'Username must be at least 3 characters.') + .max(16, 'Usernames must be no more than 16 characters.') + .regex( + new RegExp(/^[A-z0-9\_\-\.]+$/g), + 'Usernames must be alphanumeric with dashes, underscores, and periods.' + ); + +export const username = INTERNAL_username.parse; + +export const usernameAndNotUsed = INTERNAL_username.refine(async (_) => { + return !Boolean(await findUser({ username: _ })); +}, 'Username is already being used.').parseAsync; + +export const password = z + .string() + .min(6, 'Passwords must be longer than 6 characters.') + .max(128, 'You do not need a password longer than 128 fucking characters.').parse; + +export const embedTitle = z + .string() + .max(256, 'Title must not be longer than 256 characters.').parse; + +export const embedDescription = z + .string() + .max(2000, 'Description must not be longer than 2000 characters.').parse; + +export const embedColor = z + .number() + .max(parseInt('ffffff', 16), 'Color must be less than 16777215.').parse; diff --git a/src/routes/(app)/documentation/+page.svelte b/src/routes/(app)/documentation/+page.svelte new file mode 100644 index 0000000..fb7ce89 --- /dev/null +++ b/src/routes/(app)/documentation/+page.svelte @@ -0,0 +1,43 @@ + + +

I'll make real documentation later but for now have this ShareX button

+

+ MAKE SURE TO HAVE A VALID API KEY WITH THE CREATE_UPLOADS PERMISSION ( 1 << 0 ) +

+ + +{awesome} + +{#if awesome} + Download +{/if} diff --git a/src/routes/(app)/links/+page.svelte b/src/routes/(app)/links/+page.svelte deleted file mode 100644 index e69de29..0000000 diff --git a/src/routes/(app)/settings/+page.server.ts b/src/routes/(app)/settings/+page.server.ts new file mode 100644 index 0000000..f669c16 --- /dev/null +++ b/src/routes/(app)/settings/+page.server.ts @@ -0,0 +1,7 @@ +import { getUserApiKeys } from '$lib/server/database.js'; + +export async function load({ locals }) { + return { + keys: (await getUserApiKeys(locals.user.id)) || [] + }; +} diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte index ba40b1e..e377417 100644 --- a/src/routes/(app)/settings/+page.svelte +++ b/src/routes/(app)/settings/+page.svelte @@ -1,6 +1,21 @@ -
@@ -18,6 +33,8 @@ name="username" id="username" placeholder="cirro" + maxlength="16" + bind:value={username} />

Your username is used to identify you around the site. You can change it at any time. @@ -26,13 +43,14 @@

- +

Changing your email may require you to verify ownership. @@ -47,7 +65,7 @@

API Keys -

@@ -60,16 +78,9 @@ - - - d8895a0c-daa5-4b6e-b66f-2494c039fe9e - - - - - + {#each data.keys as key} + + {/each}
@@ -93,23 +104,25 @@ name="title" id="title" placeholder={`{{ file }}`} + maxlength="256" + bind:value={title} /> -

The title shown on the embed.

+

The title shown on the embed. Max 256 characters.

-

- The description of the embed. Can have up to 2000 characters. -

+

The description of the embed. Max 2000 characters.

@@ -165,8 +178,8 @@
-
- +
+

2FA

A One-Time Password will be required each time you login.

@@ -184,7 +197,7 @@
-
+
@@ -195,10 +208,10 @@
-
+
- - + +

Enable encryption for new uploads. (Client-side)

diff --git a/src/routes/(app)/uploads/+page.server.ts b/src/routes/(app)/uploads/+page.server.ts index b7c9556..3883b88 100644 --- a/src/routes/(app)/uploads/+page.server.ts +++ b/src/routes/(app)/uploads/+page.server.ts @@ -3,13 +3,11 @@ import { error } from '@sveltejs/kit'; export async function load({ locals, url }) { if (!locals.user) return error(403, { status: 403, message: 'Forbidden' }); - if (+(url.searchParams.get('i') || 0) < 0) error(400, { status: 403, message: 'Invalid Index' }); - const totalUploads = await prisma.upload.count(); + const totalUploads = await prisma.upload.count({ where: { uploaderId: locals.user.id } }); if (!totalUploads) return { uploads: [], totalUploads: 0 }; - if (+(url.searchParams.get('i') || 0) >= Math.ceil(totalUploads / 15)) error(400, { status: 403, message: 'Invalid Index' }); diff --git a/src/routes/api/+server.js b/src/routes/api/+server.ts similarity index 52% rename from src/routes/api/+server.js rename to src/routes/api/+server.ts index f750c3b..9633400 100644 --- a/src/routes/api/+server.js +++ b/src/routes/api/+server.ts @@ -1,4 +1,3 @@ -/** @type {import('./$types').RequestHandler} */ export function GET() { return new Response('OK'); } diff --git a/src/routes/api/v1/auth/login/+server.js b/src/routes/api/v1/auth/login/+server.js deleted file mode 100644 index aaf2a15..0000000 --- a/src/routes/api/v1/auth/login/+server.js +++ /dev/null @@ -1,39 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { COOKIE } from '$lib/config'; -import { findUser, createSession } from '$lib/server/database'; -import { email } from '$lib/server/validator'; -import { verifyHash } from '$lib/server/crypto'; - -/** @type {import('./$types').RequestHandler} */ -export async function POST(event) { - const { request, cookies } = event; - const body = await request.json(); - - if (!body?.email || !email(body?.email)) - return json({ error: 'Invalid email.' }, { status: 400 }); - - if (!body?.password || body?.password.length > 128 || body?.password.length < 6) - return json({ error: 'Invalid password.' }, { status: 400 }); - - const user = await findUser({ email: body?.email }); - if (!user) return json({ error: 'User record not found.' }, { status: 401 }); - - if (!(await verifyHash(user.password, body?.password))) - return json({ error: 'User record not found.' }, { status: 401 }); - - const session = await createSession(user.id); - - cookies.set(COOKIE, session.id, { path: '/' }); - - return json( - { - success: true, - data: { - id: user.id, - username: user.username, - email: user.email - } - }, - { status: 200 } - ); -} diff --git a/src/routes/api/v1/auth/login/+server.ts b/src/routes/api/v1/auth/login/+server.ts new file mode 100644 index 0000000..1c4219a --- /dev/null +++ b/src/routes/api/v1/auth/login/+server.ts @@ -0,0 +1,47 @@ +import { json } from '@sveltejs/kit'; +import { COOKIE } from '$lib/config'; +import { findUser, createSession } from '$lib/server/database'; +import { email } from '$lib/server/validator'; +import { verifyHash } from '$lib/server/crypto'; +import * as validator from '$lib/server/validator'; +import { ZodError } from 'zod'; + +export async function POST({ request, cookies }) { + const body = (await request.json()) as { + email?: string; + password?: string; + }; + + try { + const email = validator.email(body.email); + const password = validator.password(body.password); + + const user = await findUser({ email: email }); + if (!user) return json({ error: 'User record not found.' }, { status: 401 }); + + if (!(await verifyHash(user.password, password))) + return json({ error: 'User record not found.' }, { status: 401 }); + + const session = await createSession(user.id); + + cookies.set(COOKIE, session.id, { path: '/', sameSite: 'strict' }); + + return json( + { + success: true, + data: { + id: user.id, + username: user.username, + email: user.email + } + }, + { status: 200 } + ); + } catch (e) { + if (e instanceof ZodError) { + return json({ error: e.errors[0].message }, { status: 400 }); + } else { + return json({ error: 'Internal Server Error' }, { status: 500 }); + } + } +} diff --git a/src/routes/api/v1/auth/logout/+server.js b/src/routes/api/v1/auth/logout/+server.ts similarity index 75% rename from src/routes/api/v1/auth/logout/+server.js rename to src/routes/api/v1/auth/logout/+server.ts index 01dbad2..f8e53da 100644 --- a/src/routes/api/v1/auth/logout/+server.js +++ b/src/routes/api/v1/auth/logout/+server.ts @@ -2,9 +2,11 @@ import { redirect } from '@sveltejs/kit'; import { getSession, deleteSession } from '$lib/server/database'; import { COOKIE } from '$lib/config'; -/** @type {import('./$types').RequestHandler} */ export async function GET({ cookies }) { - const session = await getSession(cookies.get(COOKIE)); + const cookie = cookies.get(COOKIE); + if (!cookie) return redirect(302, '/'); + + const session = await getSession(cookie); if (!session) { cookies.delete(COOKIE, { path: '/' }); return redirect(302, '/'); diff --git a/src/routes/api/v1/auth/register/+server.js b/src/routes/api/v1/auth/register/+server.js deleted file mode 100644 index d27b763..0000000 --- a/src/routes/api/v1/auth/register/+server.js +++ /dev/null @@ -1,45 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { COOKIE } from '$lib/config'; -import { createUser, createSession, findUser } from '$lib/server/database'; -import { email } from '$lib/server/validator'; - -export async function POST(event) { - const { request, cookies } = event; - const body = await request.json(); - - if (!body?.username || body?.username.length > 16 || body?.username.length < 3) - return json({ error: 'Invalid username.' }, { status: 400 }); - - if (!body?.email || !email(body?.email)) - return json({ error: 'Invalid email.' }, { status: 400 }); - - if (!body?.password || body?.password.length > 128 || body?.password.length < 6) - return json({ error: 'Invalid password.' }, { status: 400 }); - - const usernameTaken = !!(await findUser({ username: body?.username })); - if (usernameTaken) return json({ error: 'That username is taken.' }, { status: 400 }); - - const emailUsed = !!(await findUser({ email: body?.email })); - if (emailUsed) - return json({ error: 'That email has been used too many times.' }, { status: 400 }); - - const user = await createUser(body?.username, body?.email, body?.password).catch((e) => {}); - if (!user) return json({ error: 'Internal Server Error' }, { status: 500 }); - - const session = await createSession(user.id); - if (!session) return json({ error: 'Internal Server Error' }, { status: 500 }); - - cookies.set(COOKIE, session.id, { path: '/' }); - - return json( - { - success: true, - data: { - id: user.id, - username: user.username, - email: user.email - } - }, - { status: 200 } - ); -} diff --git a/src/routes/api/v1/auth/register/+server.ts b/src/routes/api/v1/auth/register/+server.ts new file mode 100644 index 0000000..baedbcc --- /dev/null +++ b/src/routes/api/v1/auth/register/+server.ts @@ -0,0 +1,42 @@ +import { json } from '@sveltejs/kit'; +import { COOKIE } from '$lib/config'; +import { createUser, createSession } from '$lib/server/database'; +import * as validator from '$lib/server/validator'; +import { ZodError } from 'zod'; + +export async function POST({ request, cookies }) { + const body = (await request.json()) as { + username?: string; + password?: string; + email?: string; + }; + + try { + const username = await validator.usernameAndNotUsed(body.username); + const email = await validator.emailAndNotUsed(body.email); + const password = validator.password(body.password); + + const user = await createUser(username, email, password); + const session = await createSession(user.id); + + cookies.set(COOKIE, session.id, { path: '/', sameSite: 'strict' }); + + return json( + { + success: true, + data: { + id: user.id, + username: user.username, + email: user.email + } + }, + { status: 200 } + ); + } catch (e) { + if (e instanceof ZodError) { + return json({ error: e.errors[0].message }, { status: 400 }); + } else { + return json({ error: 'Internal Server Error' }, { status: 500 }); + } + } +} diff --git a/src/routes/api/v1/keys/+server.ts b/src/routes/api/v1/keys/+server.ts new file mode 100644 index 0000000..5856dc9 --- /dev/null +++ b/src/routes/api/v1/keys/+server.ts @@ -0,0 +1,29 @@ +import { API_KEY_PERMISSIONS } from '$lib/config.js'; +import { createUserApiKey, deleteUserApiKey, getUserApiKeys } from '$lib/server/database'; +import { error, json } from '@sveltejs/kit'; + +export async function GET({ locals }) { + return json(await getUserApiKeys(locals.user.id)); +} + +export async function POST({ locals }) { + return json( + await createUserApiKey( + locals.user.id, + API_KEY_PERMISSIONS.CREATE_UPLOADS | API_KEY_PERMISSIONS.READ_ACCOUNT, + new Date('2099') + ) + ); +} + +export async function DELETE({ locals, request }) { + const body = (await request.json().catch(() => {})) as { key?: string }; + if (!body?.key) error(400, { status: 400, message: 'Missing "key" value.' }); + if (!locals.user) error(401, { status: 401, message: 'Unauthorized' }); + + return json( + await deleteUserApiKey(locals.user.id, body.key).catch((_) => + error(400, { status: 400, message: 'API key does not exist.' }) + ) + ); +} diff --git a/src/routes/api/v1/statistics/+server.js b/src/routes/api/v1/statistics/+server.ts similarity index 76% rename from src/routes/api/v1/statistics/+server.js rename to src/routes/api/v1/statistics/+server.ts index aa140ba..061d8d0 100644 --- a/src/routes/api/v1/statistics/+server.js +++ b/src/routes/api/v1/statistics/+server.ts @@ -1,10 +1,9 @@ import { bytesToHumanReadable } from '$lib'; import prisma from '$lib/server/database'; -import minio, { USAGE } from '$lib/server/minio'; +import { USAGE } from '$lib/server/minio'; import { json } from '@sveltejs/kit'; import { get } from 'svelte/store'; -/** @type {import("@sveltejs/kit").RequestHandler} */ export async function GET() { return json({ users: await prisma.user.count({}), diff --git a/src/routes/api/v1/user/+server.js b/src/routes/api/v1/user/+server.js deleted file mode 100644 index 7806e5c..0000000 --- a/src/routes/api/v1/user/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -import { json } from '@sveltejs/kit'; - -/** @type {import('./$types').RequestHandler} */ -export async function GET(event) { - const { request, cookies, locals } = event; - - return json(locals?.user); -} diff --git a/src/routes/api/v1/user/+server.ts b/src/routes/api/v1/user/+server.ts new file mode 100644 index 0000000..111a696 --- /dev/null +++ b/src/routes/api/v1/user/+server.ts @@ -0,0 +1,5 @@ +import { json } from '@sveltejs/kit'; + +export async function GET({ locals }) { + return json(locals.user); +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..8b11af6 Binary files /dev/null and b/static/favicon.ico differ diff --git a/svelte.config.js b/svelte.config.js index 8b24099..802cdd3 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -4,7 +4,8 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { - adapter: adapter() + adapter: adapter(), + csrf: { checkOrigin: false } }, preprocess: vitePreprocess() }; diff --git a/yarn.lock b/yarn.lock index 0b30cc3..212d152 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1787,11 +1787,6 @@ util@^0.12.3: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -validator@^13.12.0: - version "13.12.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" - integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== - vite@^5.3.5: version "5.3.5" resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8" @@ -1870,3 +1865,8 @@ yaml@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + +zod@^3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==