diff --git a/package-lock.json b/package-lock.json index bc09be8..fe14038 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@sveltejs/adapter-static": "^3.0.1", "@sveltejs/enhanced-img": "^0.1.8", "better-sqlite3": "^9.4.3", + "cookie": "^0.6.0", "reflect-metadata": "^0.2.1", "sequelize": "^6.37.1", "sqlite3": "^5.1.7", @@ -22,6 +23,7 @@ "@sveltejs/adapter-node": "^5.0.1", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@types/cookie": "^0.6.0", "autoprefixer": "^10.4.16", "express": "^4.18.3", "flowbite": "^2.3.0", diff --git a/package.json b/package.json index 95d7a7a..92f4f85 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@sveltejs/adapter-node": "^5.0.1", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@types/cookie": "^0.6.0", "autoprefixer": "^10.4.16", "express": "^4.18.3", "flowbite": "^2.3.0", @@ -43,6 +44,7 @@ "@sveltejs/adapter-static": "^3.0.1", "@sveltejs/enhanced-img": "^0.1.8", "better-sqlite3": "^9.4.3", + "cookie": "^0.6.0", "reflect-metadata": "^0.2.1", "sequelize": "^6.37.1", "sqlite3": "^5.1.7", diff --git a/src/lib/client/api/index.ts b/src/lib/client/api/index.ts index 63ed4d9..31ab47a 100644 --- a/src/lib/client/api/index.ts +++ b/src/lib/client/api/index.ts @@ -1,8 +1,16 @@ -import type { CreateAccountRequest, CreateAccountResponse } from "$types/api"; +import type { CreateAccountRequest, CreateAccountResponse, GetUserResponse } from "$types/api"; const apiUsers = { async createAccount(body: CreateAccountRequest): Promise { - return await SuyuAPI.req("POST", "/api/user/create-account", body); + return await SuyuAPI.req("POST", "/api/user", body); + }, + + async deleteAccount() { + return await SuyuAPI.req("DELETE", "/api/user"); + }, + + async getUser(): Promise { + return await SuyuAPI.req("GET", "/api/user"); }, } as const; diff --git a/src/lib/util/api/index.ts b/src/lib/util/api/index.ts index f490364..3cafd4e 100644 --- a/src/lib/util/api/index.ts +++ b/src/lib/util/api/index.ts @@ -1,8 +1,12 @@ import { userRepo } from "$lib/server/repo"; import type { SuyuUser } from "$lib/server/schema"; +import cookie from "cookie"; export async function useAuth(request: Request | string): Promise { - const apiKey = typeof request === "string" ? request : request.headers.get("Authorization"); + const apiKey = + typeof request === "string" + ? request + : cookie.parse(request.headers.get("cookie") || "").token; if (!apiKey) { return null; } diff --git a/src/routes/account/+page.server.ts b/src/routes/account/+page.server.ts index dbc3846..000cd24 100644 --- a/src/routes/account/+page.server.ts +++ b/src/routes/account/+page.server.ts @@ -1,10 +1,10 @@ import { useAuth } from "$lib/util/api"; export async function load(opts) { - const apiKey = opts.cookies.get("api_key"); + const apiKey = opts.cookies.get("token"); const user = await useAuth(apiKey || "unused"); return { user: { ...user }, - a: "B", + token: apiKey, }; } diff --git a/src/routes/account/+page.svelte b/src/routes/account/+page.svelte index fb19204..1c74020 100644 --- a/src/routes/account/+page.svelte +++ b/src/routes/account/+page.svelte @@ -7,28 +7,50 @@ export let data: PageData; let usernameToCreate: string; + let createBtn: HTMLButtonElement; async function createAccount() { + createBtn.disabled = true; const response = await SuyuAPI.users.createAccount({ username: usernameToCreate }); if (response.success) { data = { ...(data || {}), user: response.user, + token: response.token, }; - // add api_key cookie - document.cookie = `api_key=${response.token}; path=/`; + // add token cookie + document.cookie = `token=${response.token}; path=/`; } else { alert("Failed to create account: " + response.error); window.location.reload(); } + usernameToCreate = ""; + createBtn.disabled = false; + } + + async function deleteAccount() { + const response = await SuyuAPI.users.deleteAccount(); + if (response.success) { + data = { + ...(data || {}), + // @ts-expect-error since we're deleting the account, we can't expect the user to still exist + user: undefined, + token: undefined, + }; + // remove token cookie + document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + } else { + alert("Failed to delete account: " + response.error); + } }

Account Settings

- {#if data?.user} + {#if data?.token && data?.user && data.user.username}

Username: {data.user.username}

+

Token: {data.token}

{:else}

It appears you don't have an account; please register one to access suyu's online @@ -36,10 +58,13 @@

{/if}

+
+ +
diff --git a/src/routes/api/user/create-account/+server.ts b/src/routes/api/user/+server.ts similarity index 64% rename from src/routes/api/user/create-account/+server.ts rename to src/routes/api/user/+server.ts index c0e4936..12ae845 100644 --- a/src/routes/api/user/create-account/+server.ts +++ b/src/routes/api/user/+server.ts @@ -3,7 +3,13 @@ import { userRepo } from "$lib/server/repo"; import type { SuyuUser } from "$lib/server/schema"; import { json, serializeRoles } from "$lib/server/util"; -import type { CreateAccountRequest, CreateAccountResponse } from "$types/api"; +import { useAuth } from "$lib/util/api"; +import type { + CreateAccountRequest, + CreateAccountResponse, + DeleteAccountResponse, + GetUserResponse, +} from "$types/api"; import crypto from "crypto"; import { promisify } from "util"; @@ -43,3 +49,31 @@ export async function POST({ request }) { user: createdUser, }); } + +export async function GET({ request }) { + const user = await useAuth(request); + if (!user) { + return json({ + success: false, + error: "unauthorized", + }); + } + return json({ + success: true, + user, + }); +} + +export async function DELETE({ request }) { + const user = await useAuth(request); + if (!user) { + return json({ + success: false, + error: "unauthorized", + }); + } + await userRepo.remove(user); + return json({ + success: true, + }); +} diff --git a/src/routes/api/user/self/+server.ts b/src/routes/api/user/self/+server.ts deleted file mode 100644 index 2769915..0000000 --- a/src/routes/api/user/self/+server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { json } from "$lib/server/util"; -import { useAuth } from "$lib/util/api"; -import type { GetUserResponse } from "$types/api"; - -export async function GET({ request }) { - const user = await useAuth(request); - if (!user) { - return json({ - success: false, - error: "unauthorized", - }); - } - return json({ - success: true, - user, - }); -} diff --git a/src/types/api.d.ts b/src/types/api.d.ts index 383139d..307c4ae 100644 --- a/src/types/api.d.ts +++ b/src/types/api.d.ts @@ -2,6 +2,11 @@ import type { SuyuUser } from "$lib/server/schema"; +export interface GenericFailureResponse { + success: false; + error: string; +} + export interface CreateAccountRequest { username: string; } @@ -11,22 +16,17 @@ export interface CreateAccountResponseSuccess { user: SuyuUser; token: string; } +export type CreateAccountResponse = CreateAccountResponseSuccess | GenericFailureResponse; -export interface CreateAccountResponseFailure { - success: false; - error: string; +export interface DeleteAccountResponseSuccess { + success: true; } -export type CreateAccountResponse = CreateAccountResponseSuccess | CreateAccountResponseFailure; +export type DeleteAccountResponse = DeleteAccountResponseSuccess | GenericFailureResponse; export interface GetUserResponseSuccess { success: true; user: SuyuUser; } -export interface GetUserResponseFailure { - success: false; - error: string; -} - -export type GetUserResponse = GetUserResponseSuccess | GetUserResponseFailure; +export type GetUserResponse = GetUserResponseSuccess | GenericFailureResponse;