diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bba427d..0662f03 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,4 +38,4 @@ jobs: with: project: 'spiel-place' entrypoint: 'mod.ts' - root: 'build' \ No newline at end of file + root: 'build' diff --git a/bun.lockb b/bun.lockb index 55fe5bb..3dfa21c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 05b61d6..e0ae052 100644 --- a/package.json +++ b/package.json @@ -1,46 +1,47 @@ { - "name": "talkomatic", - "version": "0.0.1", - "devDependencies": { - "@deno/kv": "^0.8.1", - "@hono/zod-validator": "^0.2.2", - "@olli/kvdex": "npm:@jsr/olli__kvdex", - "@oxi/option": "npm:@jsr/oxi__option", - "@oxi/result": "npm:@jsr/oxi__result", - "@petamoriken/float16": "^3.8.7", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@ts-rex/argon2": "npm:@jsr/ts-rex__argon2", - "arctic": "^1.9.2", - "autoprefixer": "^10.4.20", - "daisyui": "^4.12.10", - "hono": "^4.5.5", - "nanoid": "^5.0.7", - "oslo": "^1.2.1", - "postcss": "^8.4.41", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.5", - "svelte": "^5.0.0-next.1", - "svelte-check": "^3.6.0", - "sveltekit-adapter-deno": "^0.12.1", - "sveltekit-superforms": "^2.17.0", - "tailwindcss": "^3.4.10", - "typescript": "^5.0.0", - "vite": "^5.0.3", - "zod": "^3.23.8" - }, - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "format": "prettier --write .", - "lint": "prettier --check ." - }, - "trustedDependencies": [ - "svelte-preprocess" - ], - "type": "module" + "name": "talkomatic", + "version": "0.0.1", + "devDependencies": { + "@deno/kv": "^0.8.1", + "@hono/zod-validator": "^0.2.2", + "@olli/kvdex": "npm:@jsr/olli__kvdex", + "@oxi/option": "npm:@jsr/oxi__option", + "@oxi/result": "npm:@jsr/oxi__result", + "@petamoriken/float16": "^3.8.7", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@ts-rex/argon2": "npm:@jsr/ts-rex__argon2", + "arctic": "^1.9.2", + "autoprefixer": "^10.4.20", + "daisyui": "^4.12.10", + "hono": "^4.5.5", + "jose": "^5.6.3", + "nanoid": "^5.0.7", + "oslo": "^1.2.1", + "postcss": "^8.4.41", + "prettier": "^3.3.2", + "prettier-plugin-svelte": "^3.2.5", + "svelte": "^5.0.0-next.1", + "svelte-check": "^3.6.0", + "sveltekit-adapter-deno": "^0.12.1", + "sveltekit-superforms": "^2.17.0", + "tailwindcss": "^3.4.10", + "typescript": "^5.0.0", + "vite": "^5.0.3", + "zod": "^3.23.8" + }, + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check ." + }, + "trustedDependencies": [ + "svelte-preprocess" + ], + "type": "module" } diff --git a/postcss.config.js b/postcss.config.js index 2e7af2b..35237eb 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {} + } } diff --git a/src/app.d.ts b/src/app.d.ts index 6b08e8e..39b88f3 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,9 +1,9 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces -import type { FlatDocumentData } from "@olli/kvdex" -import { user, session } from "$lib/server/db" -import z from "zod" +import type { FlatDocumentData } from '@olli/kvdex' +import { user, session } from '$lib/server/db' +import z from 'zod' declare global { namespace App { // interface Error {} @@ -12,10 +12,10 @@ declare global { // interface PageState {} // interface Platform {} interface Locals { - user: FlatDocumentData, string> | null; - session: FlatDocumentData, string> | null; + user: FlatDocumentData, string> | null + session: FlatDocumentData, string> | null } } } -export { }; +export {} diff --git a/src/app.pcss b/src/app.pcss index bd6213e..b5c61c9 100644 --- a/src/app.pcss +++ b/src/app.pcss @@ -1,3 +1,3 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 9841ca8..b4473fd 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,33 +1,33 @@ -import { cookieController, cookieExpiration, getUserAndSession } from "$lib/server/auth"; -import type { Handle } from "@sveltejs/kit"; -import { createDate } from "oslo"; +import { cookieController, cookieExpiration, getUserAndSession } from '$lib/server/auth' +import type { Handle } from '@sveltejs/kit' +import { createDate } from 'oslo' export const handle: Handle = async ({ event, resolve }) => { - const sessionId = event.cookies.get("auth_session"); + const sessionId = event.cookies.get('auth_session') if (!sessionId) { - event.locals.user = null; - event.locals.session = null; - return resolve(event); + event.locals.user = null + event.locals.session = null + return resolve(event) } - const res = await getUserAndSession(sessionId); - if(res.isNone()) { - const sessionCookie = cookieController.createBlankCookie(); + const res = await getUserAndSession(sessionId) + if (res.isNone()) { + const sessionCookie = cookieController.createBlankCookie() event.cookies.set(sessionCookie.name, sessionCookie.value, { - path: ".", + path: '.', ...sessionCookie.attributes - }); - event.locals.user = null; - event.locals.session = null; - return resolve(event); - } - const { session, user } = res.unwrap() - const sessionCookie = cookieController.createCookie(session.id) - event.cookies.set(sessionCookie.name, sessionCookie.value, { - path: ".", - ...sessionCookie.attributes - }) - event.locals.user = user; - event.locals.session = session; - return resolve(event); -}; \ No newline at end of file + }) + event.locals.user = null + event.locals.session = null + return resolve(event) + } + const { session, user } = res.unwrap() + const sessionCookie = cookieController.createCookie(session.id) + event.cookies.set(sessionCookie.name, sessionCookie.value, { + path: '.', + ...sessionCookie.attributes + }) + event.locals.user = user + event.locals.session = session + return resolve(event) +} diff --git a/src/lib/Tab.svelte b/src/lib/Tab.svelte index 986f79b..c4a2585 100644 --- a/src/lib/Tab.svelte +++ b/src/lib/Tab.svelte @@ -1,14 +1,23 @@ \ No newline at end of file + diff --git a/src/lib/apiclient.ts b/src/lib/apiclient.ts index 18748e9..b40c488 100644 --- a/src/lib/apiclient.ts +++ b/src/lib/apiclient.ts @@ -1,10 +1,10 @@ -import type { api as API } from "./server/hono" -import { hc } from "hono/client" +import type { api as API } from './server/hono' +import { hc } from 'hono/client' export const api = hc('/api', { - async fetch(input: RequestInfo | URL, init?: RequestInit): Promise { - return await fetch(input, { - ...init, - credentials: 'include', - }) - } -}) \ No newline at end of file + async fetch(input: RequestInfo | URL, init?: RequestInit): Promise { + return await fetch(input, { + ...init, + credentials: 'include' + }) + } +}) diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 074566a..7a82808 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -1,12 +1,10 @@ import type { z } from 'zod' import { db, session } from './db' -import { Err, Ok, Result } from '@oxi/result' import { Option, None, Some } from '@oxi/option' import type { FlatDocumentData } from '@olli/kvdex' -import { nanoid } from 'nanoid' import { TimeSpan, createDate } from 'oslo' import { alphabet, generateRandomString } from 'oslo/crypto' -import { CookieController } from "oslo/cookie" +import { CookieController } from 'oslo/cookie' const sessionTimeSpan = new TimeSpan(1, 'w') @@ -32,9 +30,7 @@ async function deleteSession(sessionId: string): Promise { await db.session.delete(sessionId) } -async function getUserAndSession( - sessionId: string -): Promise< +async function getUserAndSession(sessionId: string): Promise< Option<{ user: FlatDocumentData, string> session: FlatDocumentData, string> @@ -44,17 +40,25 @@ async function getUserAndSession( if (!session) return None const user = (await db.user.find(session.userId))?.flat() if (!user) return None - await db.session.update(sessionId, { - expiresAt: createDate(sessionTimeSpan) - }, { expireIn: sessionTimeSpan.milliseconds() }) - return Some({ user, session }) + await db.session.update( + sessionId, + { + expiresAt: createDate(sessionTimeSpan) + }, + { expireIn: sessionTimeSpan.milliseconds() } + ) + return Some({ user, session }) } export const cookieExpiration = new TimeSpan(365 * 2, 'd') -export const cookieController = new CookieController('auth_session', { - httpOnly: true, - secure: true, - sameSite: "lax", - path: "/", -}, { expiresIn: cookieExpiration }) +export const cookieController = new CookieController( + 'auth_session', + { + httpOnly: true, + secure: true, + sameSite: 'lax', + path: '/' + }, + { expiresIn: cookieExpiration } +) export { createSessionForUser, deleteSession, getUserAndSession } diff --git a/src/lib/server/db.ts b/src/lib/server/db.ts index 906790e..b971ef5 100644 --- a/src/lib/server/db.ts +++ b/src/lib/server/db.ts @@ -1,50 +1,51 @@ -import { kvdex as kvdex, collection, model } from "@olli/kvdex" -import { openKv } from "@deno/kv" -import { z } from "zod" +import { kvdex as kvdex, collection, model } from '@olli/kvdex' +import { openKv } from '@deno/kv' +import { z } from 'zod' export const user = z.object({ - displayName: z.string(), - username: z.string(), - id: z.string(), - password: z.optional(z.string()), - oauth_github_id: z.optional(z.string()), - oauth_google_id: z.optional(z.string()), - oauth_discord_id: z.optional(z.string()) + displayName: z.string(), + username: z.string(), + id: z.string(), + password: z.optional(z.string()), + oauth_github_id: z.optional(z.string()), + oauth_google_id: z.optional(z.string()), + oauth_discord_id: z.optional(z.string()) }) export const publicUser = user.pick({ displayName: true, id: true }) export const session = z.object({ - expiresAt: z.date(), - userId: z.string(), + expiresAt: z.date(), + userId: z.string() }) export const chat = z.object({ - name: z.string(), - creator: z.string().describe('id'), - createdAt: z.date() + name: z.string(), + creator: z.string().describe('id'), + createdAt: z.date() }) export const kv = await openKv('http://0.0.0.0:4512') export const db = kvdex(kv, { - user: collection(user, { - idGenerator: ({ id }) => id, - indices: { - username: 'primary', - oauth_github_id: 'primary', - oauth_google_id: 'primary', - oauth_discord_id: 'primary' - } - }), - session: collection(session, { - indices: { - userId: 'secondary' - } - }), - chat: { - boxes: collection(model<{ roomID: string, userID: string, text: string }>()), - users: collection(publicUser), - updatekey: collection(model()), - data: collection(chat) - } -}) \ No newline at end of file + user: collection(user, { + idGenerator: ({ id }) => id, + indices: { + username: 'primary', + oauth_github_id: 'primary', + oauth_google_id: 'primary', + oauth_discord_id: 'primary' + } + }), + session: collection(session, { + indices: { + userId: 'secondary' + } + }), + chat: { + boxes: collection(model<{ roomID: string; userID: string; text: string }>()), + users: collection(publicUser), + updatekey: collection(model()), + data: collection(chat) + }, + saved_oauth_data: collection(model<{ type: 'create' | 'link'; oauth_id: string }>()) +}) diff --git a/src/lib/server/hono.ts b/src/lib/server/hono.ts index 5776f08..8e70ea5 100644 --- a/src/lib/server/hono.ts +++ b/src/lib/server/hono.ts @@ -40,18 +40,18 @@ const api = new Hono<{ Bindings: Bindings }>() return text(roomId) } ) - .get('/rooms/connect/:id', (c) => { - return streamSSE(c, async (stream) => { - while (true) { - const message = `It is ${new Date().toISOString()}` - await stream.writeSSE({ - data: message, - event: 'time-update', - }) - await stream.sleep(1000) - } - }) - }) + .get('/rooms/connect/:id', (c) => { + return streamSSE(c, async (stream) => { + while (true) { + const message = `It is ${new Date().toISOString()}` + await stream.writeSSE({ + data: message, + event: 'time-update' + }) + await stream.sleep(1000) + } + }) + }) export type api = typeof api diff --git a/src/lib/server/oauth.ts b/src/lib/server/oauth.ts index b9e2ae5..7d424aa 100644 --- a/src/lib/server/oauth.ts +++ b/src/lib/server/oauth.ts @@ -1,131 +1,211 @@ -import { Google, Discord, GitHub, type OAuth2Provider, type OAuth2ProviderWithPKCE, generateState, generateCodeVerifier } from "arctic" -import { error, redirect, type Actions, type RequestHandler, type ServerLoad } from "@sveltejs/kit"; -import type { z } from "zod"; -import type { publicUser } from "./db"; +import { Google, Discord, GitHub, generateState, generateCodeVerifier } from 'arctic' +import { error, redirect, type Actions, type RequestHandler, type ServerLoad } from '@sveltejs/kit' +import { z } from 'zod' +import { db, publicUser } from './db' +import { env } from '$env/dynamic/private' +import { dev } from '$app/environment' +import { alphabet, generateRandomString } from 'oslo/crypto' +import { decodeJwt } from 'jose' +import { superForm, type SuperForm } from 'sveltekit-superforms' +import { zod, type ValidationAdapter } from 'sveltekit-superforms/adapters' + +const { + DISCORD_CLIENT_ID, + DISCORD_CLIENT_SECRET, + GITHUB_CLIENT_ID, + GITHUB_CLIENT_SECRET, + GOOGLE_CLIENT_ID, + GOOGLE_CLIENT_SECRET +} = env //TODO: oauth -export const google = new Google(); -export const discord = new Discord(); -export const github = new GitHub(); +const DEVURL = (prov: string) => `http://localhost:5173/oauth/${prov}/callback` +const PRODURL = (prov: string) => `https://spiel.place/oauth/${prov}/callback` + +export const github = new GitHub(GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, { + redirectURI: dev ? DEVURL('github') : PRODURL('github') +}) +export const discord = new Discord( + DISCORD_CLIENT_ID, + DISCORD_CLIENT_SECRET, + dev ? DEVURL('discord') : PRODURL('discord') +) +export const google = new Google( + GOOGLE_CLIENT_ID, + GOOGLE_CLIENT_SECRET, + dev ? DEVURL('google') : PRODURL('google') +) export function oauth_handler(): RequestHandler<{ provider: string }> { - return async ({ cookies, params: { provider: providerID }, url }) => { - let provider: Google | Discord | GitHub; - let scopes: string[] - let noop = false; - switch(providerID) { - case "discord": { - provider = discord - scopes = ['identify'] - break - } - case "google": { - provider = google - scopes = ['profile'] - break - } - case "github": { - provider = github - scopes = [] - break - } - default: { - noop = true - break - } - } - // @ts-expect-error: I know im using it before assignment that's the point - if(noop || !provider || !scopes) error(404, "provider not found") - let redir: URL; - const state = generateState(); - let codeVerifier: string; - if(provider instanceof Google) { - codeVerifier = generateCodeVerifier() - redir = await provider.createAuthorizationURL(state, codeVerifier, { - scopes - }) - } else { - redir = await provider.createAuthorizationURL(state, { - scopes - }) - } - cookies.set("state", state, { - secure: true, - path: "/", - httpOnly: true, - maxAge: 60 * 10 - }); - // @ts-expect-error: I know im using it before assignment that's the point - if(codeVerifier) { - cookies.set("code_verifier", codeVerifier, { - secure: true, - path: "/", - httpOnly: true, - maxAge: 60 * 10 - }); - } - redirect(302, redir); - } + return async ({ cookies, params: { provider: providerID }, url }) => { + let provider: Google | Discord | GitHub + let scopes: string[] + let noop = false + switch (providerID) { + case 'discord': { + provider = discord + scopes = ['identify'] + break + } + case 'google': { + provider = google + scopes = ['profile'] + break + } + case 'github': { + provider = github + scopes = [] + break + } + default: { + noop = true + break + } + } + // @ts-expect-error: I know im using it before assignment that's the point + if (noop || !provider || !scopes) error(404, 'provider not found') + let redir: URL + const state = generateState() + let codeVerifier: string + if (provider instanceof Google) { + codeVerifier = generateCodeVerifier() + redir = await provider.createAuthorizationURL(state, codeVerifier, { + scopes + }) + } else { + redir = await provider.createAuthorizationURL(state, { + scopes + }) + } + cookies.set('state', state, { + secure: true, + path: '/', + httpOnly: true, + maxAge: 60 * 10 + }) + // @ts-expect-error: I know im using it before assignment that's the point + if (codeVerifier) { + cookies.set('code_verifier', codeVerifier, { + secure: true, + path: '/', + httpOnly: true, + maxAge: 60 * 10 + }) + } + redirect(302, redir) + } } -export function oauth_callback(): ServerLoad<{ provider: string }, any, { type: "create" | "link", name: string, user: z.infer }> { - return async ({ cookies, params: { provider: providerID }, locals, url }) => { - let provider: Google | Discord | GitHub; - let scopes: string[] - let noop = false; - switch(providerID) { - case "discord": { - provider = discord - scopes = ['identify'] - break - } - case "google": { - provider = google - scopes = ['profile'] - break - } - case "github": { - provider = github - scopes = [] - break - } - default: { - noop = true - break - } - } - // @ts-expect-error: I know im using it before assignment that's the point - if(noop || !provider || !scopes) error(404, "provider not found") - const code = url.searchParams.get("code"); - const state = url.searchParams.get("state"); - - const storedState = cookies.get("state"); - const storedCodeVerifier = cookies.get("code_verifier"); - if (!code || !storedState || state !== storedState || (provider instanceof Google && !storedCodeVerifier)) { - error(400, "Invalid request") - } - let tokens - if(provider instanceof Google) { - tokens = await provider.validateAuthorizationCode(code, storedCodeVerifier) - } else { - tokens = await provider.validateAuthorizationCode(code) - } - if(locals.user) { - // the user is already logged in, ask them if they want to link the account to their existing account, or log out and try again - return { - type: 'create', +export function oauth_callback(): ServerLoad< + { provider: string }, + any, + { + type: 'create' | 'link' + name: string + user: z.infer + form: SuperForm< + ValidationAdapter< + { + token: string + }, + { + token: string + } + >, + any + > + prov: string + } +> { + return async ({ cookies, params: { provider: providerID }, locals, url }) => { + let provider: Google | Discord | GitHub + let scopes: string[] + let noop = false + switch (providerID) { + case 'discord': { + provider = discord + scopes = ['identify'] + break + } + case 'google': { + provider = google + scopes = ['profile'] + break + } + case 'github': { + provider = github + scopes = [] + break + } + default: { + noop = true + break + } + } + // @ts-expect-error: I know im using it before assignment that's the point + if (noop || !provider || !scopes) error(404, 'provider not found') + const code = url.searchParams.get('code') + const state = url.searchParams.get('state') - } - } else { - // the user is NOT logged in, log them in, if there is no account linked to that provided user, ask them if they want to create an account - - } - } + const storedState = cookies.get('state') + const storedCodeVerifier = cookies.get('code_verifier') + if ( + !code || + !storedState || + state !== storedState || + (provider instanceof Google && !storedCodeVerifier) + ) { + error(400, 'Invalid request') + } + let tokens + let id + let name + if (provider instanceof Google) { + tokens = await provider.validateAuthorizationCode(code, storedCodeVerifier) + console.log(tokens.idToken) + const { sub, name: Uname } = decodeJwt(tokens.idToken) + id = sub + name = Uname + } else { + tokens = await provider.validateAuthorizationCode(code) + } + const formToken = generateRandomString(12, alphabet('0-9', 'a-z')) + const form = superForm( + zod(z.object({ token: z.string() }), { + defaults: { token: formToken } + }) + ) + if (locals.user) { + // the user is already logged in, ask them if they want to link the account to their existing account, or log out and try again + await db.saved_oauth_data.set(formToken, { + oauth_id: id, + type: 'link' + }) + return { + type: 'link', + name, + prov: providerID, + user: publicUser.safeParse(locals.user).data!, + form + } + } else { + // the user is NOT logged in, log them in, if there is no account linked to that provided user, ask them if they want to create an account + await db.saved_oauth_data.set(formToken, { + oauth_id: id, + type: 'create' + }) + return { + type: 'create', + name, + prov: providerID, + user: publicUser.safeParse(locals.user).data!, + form + } + } + } } -export function callback_actions(): Actions<{ provider: string }> { - return { - - } -} \ No newline at end of file +export function oauth_callback_actions(): Actions<{ provider: string }> { + return {} +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1af3ad6..1161996 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,6 +1,6 @@ -{@render children()} \ No newline at end of file +{@render children()} diff --git a/src/routes/+page.ts b/src/routes/+page.ts index f7d2f63..cd00a67 100644 --- a/src/routes/+page.ts +++ b/src/routes/+page.ts @@ -1,5 +1,5 @@ -import { redirect } from "@sveltejs/kit"; +import { redirect } from '@sveltejs/kit' export async function load() { - return redirect(302, '/app') -} \ No newline at end of file + return redirect(302, '/app') +} diff --git a/src/routes/api/[...path]/+server.ts b/src/routes/api/[...path]/+server.ts index 8a3a252..dd06038 100644 --- a/src/routes/api/[...path]/+server.ts +++ b/src/routes/api/[...path]/+server.ts @@ -1,5 +1,5 @@ -import { hono } from '$lib/server/hono.js'; +import { hono } from '$lib/server/hono' export async function fallback({ request, locals }) { - return hono.fetch(request, { locals }) -} \ No newline at end of file + return hono.fetch(request, { locals }) +} diff --git a/src/routes/app/+layout.server.ts b/src/routes/app/+layout.server.ts index dd65cc0..faeee3b 100644 --- a/src/routes/app/+layout.server.ts +++ b/src/routes/app/+layout.server.ts @@ -1,12 +1,12 @@ -import { publicUser } from '$lib/server/db.js'; -import { redirect } from '@sveltejs/kit'; +import { publicUser } from '$lib/server/db' +import { redirect } from '@sveltejs/kit' export async function load({ locals }) { - if(!locals.session) { - return redirect(302, "/auth") - } - return publicUser.safeParse(locals.user).data! + if (!locals.session) { + return redirect(302, '/auth') + } + return publicUser.safeParse(locals.user).data! } -export const csr = true; -export const ssr = false; \ No newline at end of file +export const csr = true +export const ssr = false diff --git a/src/routes/app/+layout.svelte b/src/routes/app/+layout.svelte index d05b9aa..d74b2b8 100644 --- a/src/routes/app/+layout.svelte +++ b/src/routes/app/+layout.svelte @@ -5,73 +5,92 @@
-
- -
-
- {@render children()} -
+
+ +
+
+ {@render children()} +
-
- New message arrived. -
-
\ No newline at end of file +
+ New message arrived. +
+ diff --git a/src/routes/app/+page.svelte b/src/routes/app/+page.svelte index caf23ce..bb70fc6 100644 --- a/src/routes/app/+page.svelte +++ b/src/routes/app/+page.svelte @@ -1,29 +1,33 @@
-
- new room - - -
+
+ new room + + +
-
- list -
\ No newline at end of file +
list
diff --git a/src/routes/app/:[roomID]/+page.svelte b/src/routes/app/:[roomID]/+page.svelte index 775080e..649e0e3 100644 --- a/src/routes/app/:[roomID]/+page.svelte +++ b/src/routes/app/:[roomID]/+page.svelte @@ -1,5 +1,5 @@ -{data.roomID} \ No newline at end of file +{data.roomID} diff --git a/src/routes/app/:[roomID]/+page.ts b/src/routes/app/:[roomID]/+page.ts index 2328ef5..2afb1b9 100644 --- a/src/routes/app/:[roomID]/+page.ts +++ b/src/routes/app/:[roomID]/+page.ts @@ -1,3 +1,3 @@ export async function load({ params: { roomID } }) { - return { roomID } -} \ No newline at end of file + return { roomID } +} diff --git a/src/routes/auth/+page.server.ts b/src/routes/auth/+page.server.ts index e072409..60d0780 100644 --- a/src/routes/auth/+page.server.ts +++ b/src/routes/auth/+page.server.ts @@ -1,75 +1,80 @@ -import { alphabet, generateRandomString } from 'oslo/crypto'; -import { fail, message, setError, superValidate } from 'sveltekit-superforms'; -import { zod } from 'sveltekit-superforms/adapters'; -import { z } from 'zod'; -import { hash, verify } from "@ts-rex/argon2" -import { db } from '$lib/server/db.js'; -import { cookieController, cookieExpiration, createSessionForUser } from '$lib/server/auth.js'; -import { createDate } from 'oslo'; -import { redirect } from '@sveltejs/kit'; +import { alphabet, generateRandomString } from 'oslo/crypto' +import { fail, message, setError, superValidate } from 'sveltekit-superforms' +import { zod } from 'sveltekit-superforms/adapters' +import { z } from 'zod' +import { hash, verify } from '@ts-rex/argon2' +import { db } from '$lib/server/db' +import { cookieController, cookieExpiration, createSessionForUser } from '$lib/server/auth' +import { createDate } from 'oslo' +import { redirect } from '@sveltejs/kit' const schema = z.object({ - username: z.string().min(4, "must be atleast 4 characters").max(32, "must be less than 32 characters").regex(/^[a-z0-9_\-]+$/i, `must be alphanumeric, with the exception of "_" and "-"`), - password: z.string().min(8, "must be atleast 8 characters").max(255) -}); + username: z + .string() + .min(4, 'must be atleast 4 characters') + .max(32, 'must be less than 32 characters') + .regex(/^[a-z0-9_\-]+$/i, `must be alphanumeric, with the exception of "_" and "-"`), + password: z.string().min(8, 'must be atleast 8 characters').max(255) +}) export async function load({ locals }) { - if(locals.session) { - return redirect(302, '/app') - } - const form = await superValidate(zod(schema)); - return { form }; -}; + if (locals.session) { + return redirect(302, '/app') + } + const form = await superValidate(zod(schema)) + return { form } +} export const actions = { - login: async ({ request, cookies }) => { - const form = await superValidate(request, zod(schema)); + login: async ({ request, cookies }) => { + const form = await superValidate(request, zod(schema)) - if (!form.valid) return fail(400, { form }); - const { username, password } = form.data - const user = (await db.user.findByPrimaryIndex('username', username))?.flat(); - if (!user) return setError(form, "user does not exist") - if (!user.password) return setError(form, "this account does not have a password, maybe try a different method?") - const isvalid = verify(password, user.password); - if (!isvalid) return setError(form, "incorrect password") - const session = (await createSessionForUser(user.id)) - if (session.isSome()) { - const sessionCookie = cookieController.createCookie(session.unwrap().id) - cookies.set(sessionCookie.name, sessionCookie.value, { - path: ".", - ...sessionCookie.attributes - }) - return redirect(302, '/app') - } else { - return fail(500, { form }) - } - }, - signup: async ({ request, cookies }) => { - const form = await superValidate(request, zod(schema)); - if (!form.valid) return fail(400, { form }); - const { username, password } = form.data + if (!form.valid) return fail(400, { form }) + const { username, password } = form.data + const user = (await db.user.findByPrimaryIndex('username', username))?.flat() + if (!user) return setError(form, 'user does not exist') + if (!user.password) + return setError(form, 'this account does not have a password, maybe try a different method?') + const isvalid = verify(password, user.password) + if (!isvalid) return setError(form, 'incorrect password') + const session = await createSessionForUser(user.id) + if (session.isSome()) { + const sessionCookie = cookieController.createCookie(session.unwrap().id) + cookies.set(sessionCookie.name, sessionCookie.value, { + path: '.', + ...sessionCookie.attributes + }) + return redirect(302, '/app') + } else { + return fail(500, { form }) + } + }, + signup: async ({ request, cookies }) => { + const form = await superValidate(request, zod(schema)) + if (!form.valid) return fail(400, { form }) + const { username, password } = form.data - const userId = generateRandomString(10, alphabet("0-9", "a-z")) - const passwordHash = hash(password) + const userId = generateRandomString(10, alphabet('0-9', 'a-z')) + const passwordHash = hash(password) - const user = (await db.user.findByPrimaryIndex('username', username))?.flat() - if (user) return setError(form, "username", 'username already exists') - await db.user.set(userId, { - displayName: username, - username, - id: userId, - password: passwordHash - }) - const session = (await createSessionForUser(userId)) - if (session.isSome()) { - const sessionCookie = cookieController.createCookie(session.unwrap().id) - cookies.set(sessionCookie.name, sessionCookie.value, { - path: ".", - ...sessionCookie.attributes - }) - return redirect(302, '/app') - } else { - return fail(500, { form }) - } - } -} \ No newline at end of file + const user = (await db.user.findByPrimaryIndex('username', username))?.flat() + if (user) return setError(form, 'username', 'username already exists') + await db.user.set(userId, { + displayName: username, + username, + id: userId, + password: passwordHash + }) + const session = await createSessionForUser(userId) + if (session.isSome()) { + const sessionCookie = cookieController.createCookie(session.unwrap().id) + cookies.set(sessionCookie.name, sessionCookie.value, { + path: '.', + ...sessionCookie.attributes + }) + return redirect(302, '/app') + } else { + return fail(500, { form }) + } + } +} diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index 2a8aaaa..1805fdd 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -18,9 +18,9 @@

sign up/in

or
+ class:showerror={$errors.username}>{$errors.username?.join(' & ')} + class:showerror={$errors.password}>{$errors.password}
+{:else if data.type === 'link'}{/if} diff --git a/src/routes/oauth/[provider]/callback/+server.ts b/src/routes/oauth/[provider]/callback/+server.ts deleted file mode 100644 index e69de29..0000000 diff --git a/svelte.config.js b/svelte.config.js index d6f38cf..0118511 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,7 +1,7 @@ -import adapter from 'sveltekit-adapter-deno'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import adapter from 'sveltekit-adapter-deno' +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' -import { Float16Array } from "@petamoriken/float16" +import { Float16Array } from '@petamoriken/float16' // kvdex uses float16array under the hood (doesn't exist in node), filling that in here so it works during dev globalThis.Float16Array = Float16Array @@ -23,6 +23,6 @@ const config = { } }) } -}; +} -export default config; +export default config diff --git a/tailwind.config.js b/tailwind.config.js index 08be3d2..042ca31 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,11 +1,10 @@ -import daisyui from "daisyui" +import daisyui from 'daisyui' /** @type {import('tailwindcss').Config} */ export default { - content: ['./src/**/*.{html,js,svelte,ts}'], - theme: { - extend: {}, - }, - plugins: [daisyui], + content: ['./src/**/*.{html,js,svelte,ts}'], + theme: { + extend: {} + }, + plugins: [daisyui] } - diff --git a/vite.config.ts b/vite.config.ts index 5cf7a7b..2ae69f3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,5 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; +import { sveltekit } from '@sveltejs/kit/vite' +import { defineConfig } from 'vite' export default defineConfig({ plugins: [sveltekit()], @@ -11,4 +11,4 @@ export default defineConfig({ target: ['deno1.45.5'] } } -}); +})