diff --git a/Foxnouns.Frontend/src/lib/components/URLAlert.svelte b/Foxnouns.Frontend/src/lib/components/URLAlert.svelte new file mode 100644 index 0000000..72689ab --- /dev/null +++ b/Foxnouns.Frontend/src/lib/components/URLAlert.svelte @@ -0,0 +1,14 @@ + + +{#if key} +
+ {$t(key)} +
+{/if} diff --git a/Foxnouns.Frontend/src/lib/i18n/locales/en.json b/Foxnouns.Frontend/src/lib/i18n/locales/en.json index 453e4dc..55696dd 100644 --- a/Foxnouns.Frontend/src/lib/i18n/locales/en.json +++ b/Foxnouns.Frontend/src/lib/i18n/locales/en.json @@ -77,7 +77,16 @@ "log-in-sign-up-link": "Sign up with email", "forgot-password-title": "Forgot password", "reset-password-title": "Reset password", - "password-changed-hint": "Your password has been changed!" + "password-changed-hint": "Your password has been changed!", + "link-email-header": "Link a new email address", + "unlink-email-header": "Unlink email address", + "unlink-fediverse-header": "Unlink fediverse account", + "unlink-tumblr-header": "Unlink Tumblr account", + "unlink-google-header": "Unlink Google account", + "unlink-discord-header": "Unlink Discord account", + "unlink-confirmation-1": "Are you sure you want to unlink {{username}} from your account?", + "unlink-confirmation-2": "You will no longer be able to use this account to log in. Please make sure at least one of your other linked accounts is accessible before continuing.", + "unlink-button": "Unlink account" }, "error": { "bad-request-header": "Something was wrong with your input", @@ -310,5 +319,8 @@ "form": { "optional": "(optional)", "required": "Required" + }, + "alert": { + "auth-method-remove-success": "Successfully unlinked account!" } } diff --git a/Foxnouns.Frontend/src/lib/index.ts b/Foxnouns.Frontend/src/lib/index.ts index fdf8885..42d3314 100644 --- a/Foxnouns.Frontend/src/lib/index.ts +++ b/Foxnouns.Frontend/src/lib/index.ts @@ -9,9 +9,10 @@ export const setToken = (cookies: Cookies, token: string) => cookies.set(TOKEN_COOKIE_NAME, token, { path: "/" }); export const clearToken = (cookies: Cookies) => cookies.delete(TOKEN_COOKIE_NAME, { path: "/" }); -// TODO: change this to something we actually clearly have the rights to use -export const DEFAULT_AVATAR = "https://pronouns.cc/default/512.webp"; export const DEFAULT_FLAG = "/unknown_flag.svg"; export const idTimestamp = (id: string) => DateTime.fromMillis(parseInt(id, 10) / (1 << 22) + 1_640_995_200_000); + +export const alertKey = (url: URL): string | undefined => + url.searchParams.has("alert") ? "alert." + url.searchParams.get("alert") : undefined; diff --git a/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts b/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts index 65be131..24b62a3 100644 --- a/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/settings/auth/+page.server.ts @@ -1,11 +1,12 @@ import { apiRequest, fastRequest } from "$api"; import ApiError, { ErrorCode, type RawApiError } from "$api/error.js"; import type { AuthUrls } from "$api/models/auth"; +import { alertKey } from "$lib"; import log from "$lib/log"; -export const load = async ({ fetch }) => { +export const load = async ({ fetch, url }) => { const urls = await apiRequest("POST", "/auth/urls", { fetch, isInternal: true }); - return { urls }; + return { urls, alertKey: alertKey(url) }; }; export const actions = { diff --git a/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte b/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte index c0d6056..84722b7 100644 --- a/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte +++ b/Foxnouns.Frontend/src/routes/settings/auth/+page.svelte @@ -1,6 +1,7 @@ + + {#if data.urls.email_enabled} {/if} diff --git a/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte index d2f5070..8e8d16a 100644 --- a/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte +++ b/Foxnouns.Frontend/src/routes/settings/auth/add-email/+page.svelte @@ -8,7 +8,7 @@
-

Link a new email address

+

{$t("auth.link-email-header")}

diff --git a/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.server.ts b/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.server.ts new file mode 100644 index 0000000..b2c6318 --- /dev/null +++ b/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.server.ts @@ -0,0 +1,40 @@ +import { fastRequest } from "$api"; +import ApiError, { ErrorCode } from "$api/error.js"; +import log from "$lib/log.js"; +import { error, isRedirect, redirect } from "@sveltejs/kit"; + +export const load = async ({ parent, params }) => { + const data = await parent(); + if (data.user.auth_methods.length < 2) { + error(403, { + message: "You cannot remove your last authentication method.", + status: 403, + code: ErrorCode.LastAuthMethod, + }); + } + + const authMethod = data.meUser!.auth_methods.find((m) => m.id === params.id); + if (!authMethod) { + error(404, { + message: "No authentication method with that ID found.", + status: 404, + code: ErrorCode.GenericApiError, + }); + } + + return { authMethod }; +}; + +export const actions = { + default: async ({ params, fetch, cookies }) => { + try { + fastRequest("DELETE", "/auth/methods/" + params.id, { fetch, cookies, isInternal: true }); + redirect(303, "/settings/auth?alert=auth-method-remove-success"); + } catch (e) { + if (isRedirect(e)) throw e; + if (e instanceof ApiError) return { error: e.obj }; + log.error("Could not remove auth method %s:", params.id, e); + throw e; + } + }, +}; diff --git a/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.svelte b/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.svelte new file mode 100644 index 0000000..22b40ad --- /dev/null +++ b/Foxnouns.Frontend/src/routes/settings/auth/remove-method/[id]/+page.svelte @@ -0,0 +1,44 @@ + + + + {unlinkHeader(data.authMethod.type)} • pronouns.cc + + +
+

{unlinkHeader(data.authMethod.type)}

+ {#if form?.error} + + {/if} +

+ {$t("auth.unlink-confirmation-1", { + username: data.authMethod.remote_username || data.authMethod.remote_id, + })} + {$t("auth.unlink-confirmation-2")} +

+
+ +
+