alias management

This commit is contained in:
Lea 2024-01-18 00:19:04 +01:00
parent 54917bfe70
commit c9421cebb4
Signed by: Lea
GPG key ID: 1BAFFE8347019C42
4 changed files with 137 additions and 35 deletions

BIN
public/mindblown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -1,16 +1,18 @@
"use client"; "use client";
import { fetchAllAliases } from "@/lib/actions"; import { approveAlias, deleteAlias, fetchAllAliases } from "@/lib/actions";
import GhostMessage from "@/lib/components/ui/GhostMessage"; import GhostMessage from "@/lib/components/ui/GhostMessage";
import LoadingSpinner from "@/lib/components/ui/LoadingSpinner"; import LoadingSpinner from "@/lib/components/ui/LoadingSpinner";
import { AliasEntry } from "@/lib/db"; import { AliasEntry } from "@/lib/db";
import { Button, Card, Flex, Heading, Table } from "@radix-ui/themes"; import { Button, Card, Code, Dialog, Flex, Heading, Table } from "@radix-ui/themes";
import { ListChecksIcon } from "lucide-react"; import { ListChecksIcon } from "lucide-react";
import Image from "next/image";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export default function Aliases() { export default function Aliases() {
const [aliases, setAliases] = useState<AliasEntry[] | null>(); const [aliases, setAliases] = useState<AliasEntry[] | null>();
const pending = aliases?.filter((alias) => alias.pending); const pending = aliases?.filter((alias) => alias.pending);
const active = aliases?.filter((alias) => !alias.pending);
useEffect(() => { useEffect(() => {
fetchAllAliases().then(setAliases); fetchAllAliases().then(setAliases);
@ -20,8 +22,9 @@ export default function Aliases() {
<> <>
<Heading className="pb-4">Aliases</Heading> <Heading className="pb-4">Aliases</Heading>
<Flex gap="3" direction="column">
<Card> <Card>
<Heading size="2" className="pb-2">Pending</Heading> <Heading size="3" className="pb-2">Pending</Heading>
{pending {pending
? pending.length ? pending.length
@ -41,8 +44,22 @@ export default function Aliases() {
<Table.Cell justify='start'>{alias.alias}</Table.Cell> <Table.Cell justify='start'>{alias.alias}</Table.Cell>
<Table.Cell justify='end'> <Table.Cell justify='end'>
<Flex gap='3' justify="end"> <Flex gap='3' justify="end">
<Button size="1" variant="outline">Approve</Button> <Button
<Button size="1" variant="solid">Delete</Button> size="1"
variant="outline"
onClick={() => approveAlias(alias.alias)
.then(() => { setAliases(aliases?.map((a) => a.id == alias.id ? { ...a, pending: false } : a)); })}
>
Approve
</Button>
<Button
size="1"
variant="solid"
onClick={() => deleteAlias(alias.alias)
.then(() => setAliases(aliases?.filter(a => a.id != alias.id)))}
>
Delete
</Button>
</Flex> </Flex>
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
@ -53,6 +70,68 @@ export default function Aliases() {
: <GhostMessage icon={<LoadingSpinner />} header="Loading" /> : <GhostMessage icon={<LoadingSpinner />} header="Loading" />
} }
</Card> </Card>
<Card>
<Heading size="3" className="pb-2">Active aliases</Heading>
{active
? active.length
? <Table.Root>
<Table.Header>
<Table.Row>
<Table.ColumnHeaderCell justify='start'>User</Table.ColumnHeaderCell>
<Table.ColumnHeaderCell justify='start'>Alias</Table.ColumnHeaderCell>
<Table.ColumnHeaderCell justify='end'>Actions</Table.ColumnHeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{active.map((alias) => (
<Table.Row key={alias.id}>
<Table.Cell justify='start'>{alias.address}</Table.Cell>
<Table.Cell justify='start'>{alias.alias}</Table.Cell>
<Table.Cell justify='end'>
<Flex gap='3' justify="end">
<Dialog.Root>
<Dialog.Trigger>
<Button
size="1"
variant="solid"
>
Delete
</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Title>Delete alias</Dialog.Title>
<Dialog.Description>Are you sure you want to delete <Code>{alias.alias}</Code>?</Dialog.Description>
<Flex gap="3" mt="4" justify="end">
<Dialog.Close>
<Button variant="outline">Cancel</Button>
</Dialog.Close>
<Dialog.Close>
<Button
variant="solid"
onClick={() => deleteAlias(alias.alias)
.then(() => setAliases(aliases?.filter(a => a.id != alias.id)))}
>
Delete
</Button>
</Dialog.Close>
</Flex>
</Dialog.Content>
</Dialog.Root>
</Flex>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
: <GhostMessage icon={<Image src="/mindblown.png" alt="Mind blown" width="40" height="40" />} header="No aliases" message="Hmm, that's weird..." />
: <GhostMessage icon={<LoadingSpinner />} header="Loading" />
}
</Card>
</Flex>
</> </>
); );
} }

View file

@ -1,7 +1,7 @@
"use server"; "use server";
import { getServerSession } from "next-auth"; import { getServerSession } from "next-auth";
import { AliasEntry, createAlias, database, deleteAliasEntry, getAlias, getAllAliases, getUserAliases, setUserPassword } from "./db"; import { AliasEntry, approveAliasEntry, createAlias, database, deleteAliasEntry, getAlias, getAllAliases, getUserAliases, setUserPassword } from "./db";
import { aliasesNeedApproval, isAdmin } from "./util"; import { aliasesNeedApproval, isAdmin } from "./util";
export async function fetchAllUsers(): Promise<string[]> { export async function fetchAllUsers(): Promise<string[]> {
@ -85,3 +85,11 @@ export async function deleteAlias(alias: string) {
await deleteAliasEntry(alias); await deleteAliasEntry(alias);
} }
export async function approveAlias(alias: string) {
const session = await getServerSession();
if (!session?.user?.email) throw new Error("Unauthenticated");
if (!isAdmin(session)) throw new Error("Unauthorized");
await approveAliasEntry(alias);
}

View file

@ -95,7 +95,7 @@ export function getAlias(alias: string) {
return new Promise<AliasEntry | undefined>(async (resolve, reject) => { return new Promise<AliasEntry | undefined>(async (resolve, reject) => {
const db = database('aliases'); const db = database('aliases');
db.get("SELECT id, address, alias, pending FROM aliases WHERE address = ?", alias, (err, res: AliasEntry) => { db.get("SELECT id, address, alias, pending FROM aliases WHERE alias = ?", alias, (err, res: AliasEntry) => {
db.close(); db.close();
if (err) return reject(err); if (err) return reject(err);
if (!res) return resolve(undefined); if (!res) return resolve(undefined);
@ -126,6 +126,21 @@ export function createAlias(user: string, alias: string, pending: boolean) {
}); });
} }
export function approveAliasEntry(alias: string) {
return new Promise<void>(async (resolve, reject) => {
const db = database('aliases');
db.run(
"UPDATE aliases SET pending = 0 WHERE alias = ?",
alias,
function(err: any) {
db.close();
if (err) return reject(err);
resolve();
});
});
}
export function deleteAliasEntry(alias: string) { export function deleteAliasEntry(alias: string) {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
const db = database('aliases'); const db = database('aliases');