feat(frontend): remove auth method
This commit is contained in:
		
							parent
							
								
									373d97e70a
								
							
						
					
					
						commit
						c47fc41437
					
				
					 8 changed files with 121 additions and 6 deletions
				
			
		
							
								
								
									
										14
									
								
								Foxnouns.Frontend/src/lib/components/URLAlert.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Foxnouns.Frontend/src/lib/components/URLAlert.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| <script lang="ts"> | ||||
| 	import { t } from "$lib/i18n"; | ||||
| 
 | ||||
| 	type Props = { data?: { alertKey?: string }; key?: string }; | ||||
| 
 | ||||
| 	let props: Props = $props(); | ||||
| 	let key = $derived(props.key ?? props.data?.alertKey); | ||||
| </script> | ||||
| 
 | ||||
| {#if key} | ||||
| 	<div class="alert alert-light"> | ||||
| 		{$t(key)} | ||||
| 	</div> | ||||
| {/if} | ||||
|  | @ -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!" | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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<AuthUrls>("POST", "/auth/urls", { fetch, isInternal: true }); | ||||
| 	return { urls }; | ||||
| 	return { urls, alertKey: alertKey(url) }; | ||||
| }; | ||||
| 
 | ||||
| export const actions = { | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| <script lang="ts"> | ||||
| 	import AuthMethodList from "$components/settings/AuthMethodList.svelte"; | ||||
| 	import EmailSettings from "$components/settings/EmailSettings.svelte"; | ||||
| 	import UrlAlert from "$components/URLAlert.svelte"; | ||||
| 	import type { ActionData, PageData } from "./$types"; | ||||
| 
 | ||||
| 	type Props = { data: PageData; form: ActionData }; | ||||
|  | @ -14,6 +15,8 @@ | |||
| 	let fediAccounts = $derived(data.user.auth_methods.filter((m) => m.type === "FEDIVERSE")); | ||||
| </script> | ||||
| 
 | ||||
| <UrlAlert {data} /> | ||||
| 
 | ||||
| {#if data.urls.email_enabled} | ||||
| 	<EmailSettings user={data.user} {canRemove} {max} {form} /> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| </script> | ||||
| 
 | ||||
| <div class="mx-auto w-lg-75"> | ||||
| 	<h3>Link a new email address</h3> | ||||
| 	<h3>{$t("auth.link-email-header")}</h3> | ||||
| 
 | ||||
| 	<FormStatusMarker {form} successMessage={$t("auth.check-inbox-for-link-hint")} /> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| 		} | ||||
| 	}, | ||||
| }; | ||||
|  | @ -0,0 +1,44 @@ | |||
| <script lang="ts"> | ||||
| 	import type { AuthType } from "$api/models"; | ||||
| 	import ErrorAlert from "$components/ErrorAlert.svelte"; | ||||
| 	import { t } from "$lib/i18n"; | ||||
| 	import type { ActionData, PageData } from "./$types"; | ||||
| 
 | ||||
| 	type Props = { data: PageData; form: ActionData }; | ||||
| 	let { data, form }: Props = $props(); | ||||
| 
 | ||||
| 	const unlinkHeader = (type: AuthType): string => { | ||||
| 		switch (type) { | ||||
| 			case "DISCORD": | ||||
| 				return $t("auth.unlink-discord-header"); | ||||
| 			case "GOOGLE": | ||||
| 				return $t("auth.unlink-google-header"); | ||||
| 			case "TUMBLR": | ||||
| 				return $t("auth.unlink-tumblr-header"); | ||||
| 			case "FEDIVERSE": | ||||
| 				return $t("auth.unlink-fediverse-header"); | ||||
| 			case "EMAIL": | ||||
| 				return $t("auth.unlink-email-header"); | ||||
| 		} | ||||
| 	}; | ||||
| </script> | ||||
| 
 | ||||
| <svelte:head> | ||||
| 	<title>{unlinkHeader(data.authMethod.type)} • pronouns.cc</title> | ||||
| </svelte:head> | ||||
| 
 | ||||
| <div class="mx-auto w-lg-75"> | ||||
| 	<h3>{unlinkHeader(data.authMethod.type)}</h3> | ||||
| 	{#if form?.error} | ||||
| 		<ErrorAlert error={form.error} /> | ||||
| 	{/if} | ||||
| 	<p> | ||||
| 		{$t("auth.unlink-confirmation-1", { | ||||
| 			username: data.authMethod.remote_username || data.authMethod.remote_id, | ||||
| 		})} | ||||
| 		<strong>{$t("auth.unlink-confirmation-2")}</strong> | ||||
| 	</p> | ||||
| 	<form method="POST"> | ||||
| 		<button type="submit" class="btn btn-danger">{$t("auth.unlink-button")}</button> | ||||
| 	</form> | ||||
| </div> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue