diff --git a/src/app/self-service/page.tsx b/src/app/self-service/page.tsx index ee21330..b346443 100644 --- a/src/app/self-service/page.tsx +++ b/src/app/self-service/page.tsx @@ -1,12 +1,12 @@ "use client"; -import { aliasAvailable, changeOwnPassword, createAliasSelf, fetchOwnAliases } from "@/lib/actions"; +import { aliasAvailable, changeOwnPassword, createAliasSelf, deleteAlias, fetchOwnAliases } from "@/lib/actions"; import GhostMessage from "@/lib/components/ui/GhostMessage"; import LoadingSpinner from "@/lib/components/ui/LoadingSpinner"; import { AliasEntry } from "@/lib/db"; import useWindowDimensions from "@/lib/hooks/useWindowDimensions"; import { aliasesNeedApproval } from "@/lib/util"; -import { Card, Text, Heading, Flex, TextField, Button, IconButton, Popover, Link, Tabs, Box, Grid, Dialog, Callout, Tooltip, Table, ScrollArea, DropdownMenu } from "@radix-ui/themes"; +import { Card, Text, Heading, Flex, TextField, Button, IconButton, Popover, Link, Tabs, Box, Grid, Dialog, Callout, Tooltip, Table, ScrollArea, DropdownMenu, Code } from "@radix-ui/themes"; import { AlertCircleIcon, CheckIcon, ChevronDownIcon, CopyIcon, ExternalLinkIcon, InfoIcon, LockIcon, UserIcon, UsersRound } from "lucide-react"; import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; @@ -303,6 +303,7 @@ export default function SelfService() { const alias = await createAliasSelf(`${newAliasUsername}@${newAliasDomain}`); setAliases([...(aliases ?? []), alias]); } catch(e) { + console.error(e); alert(e); } }}>Change @@ -332,7 +333,41 @@ export default function SelfService() { {alias.alias} {alias.pending ? "Pending" : "Active"} - + + + + + + + Delete alias + + Are you sure you want to delete the alias {alias.alias}? + It will become available for other users to claim immediately. + + + + + + + + + + + + )) } diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 483f73d..8bf25b5 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -1,7 +1,7 @@ "use server"; import { getServerSession } from "next-auth"; -import { AliasEntry, createAlias, database, getUserAliases, setUserPassword } from "./db"; +import { AliasEntry, createAlias, database, deleteAliasEntry, getAlias, getUserAliases, setUserPassword } from "./db"; import { aliasesNeedApproval, isAdmin } from "./util"; export async function fetchAllUsers(): Promise { @@ -66,3 +66,14 @@ export async function createAliasSelf(alias: string): Promise { pending: pending }; } + +export async function deleteAlias(alias: string) { + const session = await getServerSession(); + if (!session?.user?.email) throw new Error("Unauthenticated"); + + if (!isAdmin(session) && (await getAlias(alias))?.address != session.user.email) { + throw new Error("Unauthorized"); + } + + await deleteAliasEntry(alias); +} diff --git a/src/lib/db.ts b/src/lib/db.ts index 1f960e0..e20c0e2 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -77,6 +77,22 @@ export function getUserAliases(email: string) { }); } +export function getAlias(alias: string) { + return new Promise(async (resolve, reject) => { + const db = database('aliases'); + + db.get("SELECT id, address, alias, pending FROM aliases WHERE address = ?", alias, (err, res: AliasEntry) => { + db.close(); + if (err) return reject(err); + if (!res) return resolve(undefined); + resolve({ + ...res, + pending: !!res.pending, + }); + }); + }); +} + export function createAlias(user: string, alias: string, pending: boolean) { return new Promise(async (resolve, reject) => { const db = database('aliases'); @@ -95,3 +111,18 @@ export function createAlias(user: string, alias: string, pending: boolean) { }); }); } + +export function deleteAliasEntry(alias: string) { + return new Promise(async (resolve, reject) => { + const db = database('aliases'); + + db.run( + "DELETE FROM aliases WHERE alias = ?", + alias, + function(err: any) { + db.close(); + if (err) return reject(err); + resolve(); + }); + }); +}