mirror of
https://git.suyu.dev/suyu/website.git
synced 2025-12-27 01:34:39 +01:00
add account deletion
This commit is contained in:
parent
c5686166d7
commit
5351c54d8d
9 changed files with 110 additions and 37 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<CreateAccountResponse> {
|
||||
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<GetUserResponse> {
|
||||
return await SuyuAPI.req("GET", "/api/user");
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SuyuUser | null> {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="panel-blur main-panel">
|
||||
<h2>Account Settings</h2>
|
||||
<p>
|
||||
{#if data?.user}
|
||||
{#if data?.token && data?.user && data.user.username}
|
||||
<p>Username: {data.user.username}</p>
|
||||
<p>Token: <code>{data.token}</code></p>
|
||||
{:else}
|
||||
<p>
|
||||
It appears you don't have an account; please register one to access suyu's online
|
||||
|
|
@ -36,10 +58,13 @@
|
|||
</p>
|
||||
<div class="create-account">
|
||||
<input bind:value={usernameToCreate} type="text" placeholder="Username" />
|
||||
<button on:click={createAccount}>Create Account</button>
|
||||
<button bind:this={createBtn} on:click={createAccount}>Create Account</button>
|
||||
</div>
|
||||
{/if}
|
||||
</p>
|
||||
<div class="float-bottom-right">
|
||||
<button class="danger" on:click={deleteAccount}>Delete Account</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
@ -65,4 +90,19 @@
|
|||
.create-account {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.main-panel code {
|
||||
background-color: #222429;
|
||||
border: var(--border-primary);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.float-bottom-right {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 16px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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<GetUserResponse>({
|
||||
success: false,
|
||||
error: "unauthorized",
|
||||
});
|
||||
}
|
||||
return json<GetUserResponse>({
|
||||
success: true,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
export async function DELETE({ request }) {
|
||||
const user = await useAuth(request);
|
||||
if (!user) {
|
||||
return json<DeleteAccountResponse>({
|
||||
success: false,
|
||||
error: "unauthorized",
|
||||
});
|
||||
}
|
||||
await userRepo.remove(user);
|
||||
return json<DeleteAccountResponse>({
|
||||
success: true,
|
||||
});
|
||||
}
|
||||
|
|
@ -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<GetUserResponse>({
|
||||
success: false,
|
||||
error: "unauthorized",
|
||||
});
|
||||
}
|
||||
return json<GetUserResponse>({
|
||||
success: true,
|
||||
user,
|
||||
});
|
||||
}
|
||||
20
src/types/api.d.ts
vendored
20
src/types/api.d.ts
vendored
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue