yeah
This commit is contained in:
parent
be4aaf0dd4
commit
c90bfd4b47
|
@ -5,7 +5,7 @@ import GenericConfirmationDialog from "@/lib/components/ui/GenericConfirmationDi
|
||||||
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, Code, Dialog, Flex, Heading, Table } from "@radix-ui/themes";
|
import { Badge, 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 Image from "next/image";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
@ -96,7 +96,10 @@ export default function Aliases() {
|
||||||
{active.map((alias) => (
|
{active.map((alias) => (
|
||||||
<Table.Row key={alias.id}>
|
<Table.Row key={alias.id}>
|
||||||
<Table.Cell justify='start'>{alias.address}</Table.Cell>
|
<Table.Cell justify='start'>{alias.address}</Table.Cell>
|
||||||
<Table.Cell justify='start'>{alias.alias}</Table.Cell>
|
<Table.Cell justify='start'>
|
||||||
|
{alias.alias}
|
||||||
|
{alias.temporary && <Badge variant="soft" ml="3" color="gray">Temporary</Badge>}
|
||||||
|
</Table.Cell>
|
||||||
<Table.Cell justify='end'>
|
<Table.Cell justify='end'>
|
||||||
<Flex gap='3' justify="end">
|
<Flex gap='3' justify="end">
|
||||||
<Dialog.Root>
|
<Dialog.Root>
|
||||||
|
|
|
@ -9,13 +9,14 @@ import { sha256sum } from "@/lib/util";
|
||||||
import { Avatar, Card, Code, Flex, Grid, Heading, IconButton, Text } from "@radix-ui/themes";
|
import { Avatar, Card, Code, Flex, Grid, Heading, IconButton, Text } from "@radix-ui/themes";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
import { ArrowLeftIcon, ArrowRightIcon, AsteriskSquareIcon, GhostIcon, LogInIcon, UserCheckIcon, UserPlusIcon, UsersIcon, XIcon } from "lucide-react";
|
import { ArrowLeftIcon, ArrowRightIcon, AsteriskSquareIcon, GhostIcon, LogInIcon, UserCheckIcon, UserPlusIcon, UsersIcon, UsersRoundIcon, XIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
const LOG_ACTION_HUMAN_READABLE: { [key in AuditLogAction]?: string } = {
|
const LOG_ACTION_HUMAN_READABLE: { [key in AuditLogAction]?: string } = {
|
||||||
requestAlias: "Requested an alias",
|
requestAlias: "Requested an alias",
|
||||||
|
createTempAlias: "Generated a temporary alias",
|
||||||
createAlias: "Created an alias",
|
createAlias: "Created an alias",
|
||||||
approveAlias: "Approved an alias",
|
approveAlias: "Approved an alias",
|
||||||
deleteAlias: "Deleted an alias",
|
deleteAlias: "Deleted an alias",
|
||||||
|
@ -42,6 +43,7 @@ export default function Audit() {
|
||||||
|
|
||||||
const logActionIcons: { [key in AuditLogAction]?: React.ReactNode } = {
|
const logActionIcons: { [key in AuditLogAction]?: React.ReactNode } = {
|
||||||
requestAlias: <UsersIcon />,
|
requestAlias: <UsersIcon />,
|
||||||
|
createTempAlias: <UsersRoundIcon />,
|
||||||
createAlias: <UsersIcon />,
|
createAlias: <UsersIcon />,
|
||||||
approveAlias: <UserCheckIcon />,
|
approveAlias: <UserCheckIcon />,
|
||||||
deleteAlias: <XIcon />,
|
deleteAlias: <XIcon />,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Grid, Heading } from "@radix-ui/themes";
|
||||||
|
|
||||||
export default function SelfService() {
|
export default function SelfService() {
|
||||||
const dimensions = useWindowDimensions();
|
const dimensions = useWindowDimensions();
|
||||||
const mobileUi = dimensions.width < 1200;
|
const mobileUi = dimensions.width < 1450;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -144,7 +144,7 @@ export async function requestTemporaryAlias(
|
||||||
break;
|
break;
|
||||||
case 'random':
|
case 'random':
|
||||||
randomString = crypto
|
randomString = crypto
|
||||||
.randomBytes(12)
|
.randomBytes(8)
|
||||||
.toString('base64')
|
.toString('base64')
|
||||||
.replace(/\W/, ''); // Delete special characters
|
.replace(/\W/, ''); // Delete special characters
|
||||||
break;
|
break;
|
||||||
|
@ -195,6 +195,8 @@ export async function claimTemporaryAlias(key: string): Promise<AliasEntry> {
|
||||||
temporary: true,
|
temporary: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auditLog('createTempAlias', alias);
|
||||||
|
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
|
|
||||||
export type AuditLogAction = 'login' | 'changeOwnPassword' | 'requestAlias' | 'deleteAlias' // Unprivileged
|
export type AuditLogAction = 'login' | 'changeOwnPassword' | 'requestAlias' | 'deleteAlias' | 'createTempAlias' // Unprivileged
|
||||||
| 'createUser' | 'createAlias' | 'approveAlias' | 'deleteAlias'; // Privileged
|
| 'createUser' | 'createAlias' | 'approveAlias' | 'deleteAlias'; // Privileged
|
||||||
|
|
||||||
export type AuditLog = {
|
export type AuditLog = {
|
||||||
|
|
|
@ -68,7 +68,9 @@ export default function ManageUserButton({ email }: { email: string }) {
|
||||||
aliases.map((alias) => (
|
aliases.map((alias) => (
|
||||||
<Table.Row key={alias.id}>
|
<Table.Row key={alias.id}>
|
||||||
<Table.Cell justify="start">
|
<Table.Cell justify="start">
|
||||||
{alias.alias} {alias.pending && <Badge variant="soft" ml="3">Pending</Badge>}
|
{alias.alias}
|
||||||
|
{alias.pending && <Badge variant="soft" ml="3">Pending</Badge>}
|
||||||
|
{alias.temporary && <Badge variant="soft" ml="3" color="gray">Temporary</Badge>}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell justify="end">
|
<Table.Cell justify="end">
|
||||||
<Flex gap="3" justify="end">
|
<Flex gap="3" justify="end">
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { disposeTempAliasRequest, fetchOwnAliases, requestTemporaryAlias, claimTemporaryAlias } from "@/lib/actions";
|
import { disposeTempAliasRequest, fetchOwnAliases, requestTemporaryAlias, claimTemporaryAlias, deleteAlias } from "@/lib/actions";
|
||||||
import { GRAVATAR_DEFAULT } from "@/lib/constants";
|
import { GRAVATAR_DEFAULT } from "@/lib/constants";
|
||||||
import { AliasEntry, AliasRequestEntry } from "@/lib/db";
|
import { AliasEntry, AliasRequestEntry } from "@/lib/db";
|
||||||
|
import { ToastContext } from "@/lib/providers/ToastProvider";
|
||||||
import { sha256sum } from "@/lib/util";
|
import { sha256sum } from "@/lib/util";
|
||||||
import { Avatar, Button, Card, Dialog, Flex, Heading, IconButton, Select, Text, TextField } from "@radix-ui/themes";
|
import { Avatar, Badge, Box, Button, Card, Code, Dialog, Flex, Heading, IconButton, ScrollArea, Select, Table, Text, TextField } from "@radix-ui/themes";
|
||||||
import { CircleUserIcon, RefreshCcwIcon } from "lucide-react";
|
import { CircleUserIcon, CopyIcon, RefreshCcwIcon, UsersRoundIcon } from "lucide-react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import GhostMessage from "../GhostMessage";
|
||||||
|
import LoadingSpinner from "../LoadingSpinner";
|
||||||
|
|
||||||
export default function TempAliasesCard() {
|
export default function TempAliasesCard() {
|
||||||
const session = useSession().data;
|
const session = useSession().data;
|
||||||
|
@ -15,7 +18,8 @@ export default function TempAliasesCard() {
|
||||||
const [labelAtEnd, setLabelAtEnd] = useState(false);
|
const [labelAtEnd, setLabelAtEnd] = useState(false);
|
||||||
const [aliasStyle, setAliasStyle] = useState<"words" | "random">("words");
|
const [aliasStyle, setAliasStyle] = useState<"words" | "random">("words");
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [created, setCreated] = useState(false);
|
const [state, setState] = useState<'input' | 'loading' | 'created'>('input');
|
||||||
|
const toast = useContext(ToastContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchOwnAliases(true).then(setAliases);
|
fetchOwnAliases(true).then(setAliases);
|
||||||
|
@ -50,7 +54,7 @@ export default function TempAliasesCard() {
|
||||||
setAliasLabel("");
|
setAliasLabel("");
|
||||||
setLabelAtEnd(false);
|
setLabelAtEnd(false);
|
||||||
setAliasStyle("words");
|
setAliasStyle("words");
|
||||||
setCreated(false);
|
setState('input');
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<Dialog.Trigger>
|
<Dialog.Trigger>
|
||||||
|
@ -58,96 +62,214 @@ export default function TempAliasesCard() {
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Content>
|
<Dialog.Content>
|
||||||
<Dialog.Title>Generate alias</Dialog.Title>
|
<Dialog.Title>Generate alias</Dialog.Title>
|
||||||
<Dialog.Description>
|
|
||||||
Temporary aliases can be used to sign up with services without exposing your primary email address.
|
|
||||||
If an alias starts receiving spam or you no longer use the service, you can delete the alias.
|
|
||||||
</Dialog.Description>
|
|
||||||
|
|
||||||
<Flex direction="row" gap="3" mt="4">
|
{
|
||||||
<Flex direction="column" gap="3" grow="1">
|
state == "input"
|
||||||
<Text ml="1" size="1" color="gray" mb="-1">Alias label</Text>
|
? <>
|
||||||
<TextField.Root>
|
<Dialog.Description>
|
||||||
<TextField.Slot>
|
Temporary aliases can be used to sign up with services without exposing your primary email address.
|
||||||
<CircleUserIcon size="16" />
|
If an alias starts receiving spam or you no longer use the service, you can delete the alias.
|
||||||
</TextField.Slot>
|
</Dialog.Description>
|
||||||
<TextField.Input
|
|
||||||
placeholder="Alias label"
|
|
||||||
value={aliasLabel}
|
|
||||||
onChange={(e) => setAliasLabel(e.currentTarget.value.substring(0, 16))}
|
|
||||||
/>
|
|
||||||
</TextField.Root>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex direction="column" gap="3">
|
<Flex direction="row" gap="3" mt="4">
|
||||||
<Text ml="1" size="1" color="gray" mb="-1">Alias style</Text>
|
<Flex direction="column" gap="3" grow="1">
|
||||||
<Select.Root value={aliasStyle} onValueChange={(value) => setAliasStyle(value as any)}>
|
<Text ml="1" size="1" color="gray" mb="-1">Alias label</Text>
|
||||||
<Select.Trigger />
|
<TextField.Root>
|
||||||
<Select.Content>
|
<TextField.Slot>
|
||||||
<Select.Group>
|
<CircleUserIcon size="16" />
|
||||||
<Select.Label>Alias style</Select.Label>
|
</TextField.Slot>
|
||||||
<Select.Item value="words">Words</Select.Item>
|
<TextField.Input
|
||||||
<Select.Item value="random">Random</Select.Item>
|
placeholder="Alias label"
|
||||||
</Select.Group>
|
value={aliasLabel}
|
||||||
</Select.Content>
|
onChange={(e) => setAliasLabel(e.currentTarget.value.substring(0, 16))}
|
||||||
</Select.Root>
|
/>
|
||||||
</Flex>
|
</TextField.Root>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="column" gap="3">
|
<Flex direction="column" gap="3">
|
||||||
<Text ml="1" size="1" color="gray" mb="-1">Label position</Text>
|
<Text ml="1" size="1" color="gray" mb="-1">Alias style</Text>
|
||||||
<Select.Root value={labelAtEnd ? "after" : "before"} onValueChange={(value) => setLabelAtEnd(value == "after")}>
|
<Select.Root value={aliasStyle} onValueChange={(value) => setAliasStyle(value as any)}>
|
||||||
<Select.Trigger />
|
<Select.Trigger />
|
||||||
<Select.Content>
|
<Select.Content>
|
||||||
<Select.Group>
|
<Select.Group>
|
||||||
<Select.Label>Alias style</Select.Label>
|
<Select.Label>Alias style</Select.Label>
|
||||||
<Select.Item value="before">Label first</Select.Item>
|
<Select.Item value="words">Words</Select.Item>
|
||||||
<Select.Item value="after">Label at end</Select.Item>
|
<Select.Item value="random">Random</Select.Item>
|
||||||
</Select.Group>
|
</Select.Group>
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Card mt="4">
|
<Flex direction="column" gap="3">
|
||||||
<Flex direction="row" justify="between" align="center">
|
<Text ml="1" size="1" color="gray" mb="-1">Label position</Text>
|
||||||
<Flex direction="row" align="center" gap="3">
|
<Select.Root value={labelAtEnd ? "after" : "before"} onValueChange={(value) => setLabelAtEnd(value == "after")}>
|
||||||
<Avatar
|
<Select.Trigger />
|
||||||
size="3"
|
<Select.Content>
|
||||||
src={`https://gravatar.com/avatar/${sha256sum(aliasPreview?.alias || "")}?d=${GRAVATAR_DEFAULT}`}
|
<Select.Group>
|
||||||
fallback={"@"}
|
<Select.Label>Alias style</Select.Label>
|
||||||
/>
|
<Select.Item value="before">Label first</Select.Item>
|
||||||
<Flex direction="column" gap="0">
|
<Select.Item value="after">Label at end</Select.Item>
|
||||||
<Text size="3" weight="medium">Generated alias</Text>
|
</Select.Group>
|
||||||
<Text size="2" weight="light">{aliasPreview?.alias || "Enter a label first"}</Text>
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
|
||||||
<IconButton onClick={() => refreshAlias()} disabled={!aliasLabel} variant="surface" size="2">
|
|
||||||
<RefreshCcwIcon size="16" />
|
|
||||||
</IconButton>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Flex gap="3" mt="4" justify="end">
|
<Card mt="4">
|
||||||
<Dialog.Close>
|
<Flex direction="row" justify="between" align="center">
|
||||||
<Button variant="outline">Close</Button>
|
<Flex direction="row" align="center" gap="3">
|
||||||
</Dialog.Close>
|
<Avatar
|
||||||
<Button
|
size="3"
|
||||||
disabled={!aliasPreview}
|
src={`https://gravatar.com/avatar/${sha256sum(aliasPreview?.alias || "")}?d=${GRAVATAR_DEFAULT}`}
|
||||||
variant="soft"
|
fallback={"@"}
|
||||||
onClick={async () => {
|
/>
|
||||||
try {
|
<Flex direction="column" gap="0">
|
||||||
//const alias = await claimTemporaryAlias(aliasPreview!.key);
|
<Text size="3" weight="medium">Generated alias</Text>
|
||||||
} catch(e) {
|
<Text size="2" weight="light">{aliasPreview?.alias || "Enter a label first"}</Text>
|
||||||
setOpen(false);
|
</Flex>
|
||||||
}
|
</Flex>
|
||||||
}}
|
<IconButton onClick={() => refreshAlias()} disabled={!aliasLabel} variant="surface" size="2">
|
||||||
>
|
<RefreshCcwIcon size="16" />
|
||||||
Create
|
</IconButton>
|
||||||
</Button>
|
</Flex>
|
||||||
</Flex>
|
</Card>
|
||||||
|
|
||||||
|
<Flex gap="3" mt="4" justify="end">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button variant="outline">Close</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
<Button
|
||||||
|
disabled={!aliasPreview}
|
||||||
|
variant="soft"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
setState('loading');
|
||||||
|
const alias = await claimTemporaryAlias(aliasPreview!.key);
|
||||||
|
setAliases([...(aliases ?? []), alias]);
|
||||||
|
setState('created');
|
||||||
|
} catch (e) {
|
||||||
|
setOpen(false);
|
||||||
|
toast({
|
||||||
|
title: "Failed to create alias",
|
||||||
|
description: `${e}`,
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
: state == "loading"
|
||||||
|
? <>
|
||||||
|
<GhostMessage icon={<LoadingSpinner />} header="Creating your alias..." />
|
||||||
|
</>
|
||||||
|
: <>
|
||||||
|
<Card mt="4">
|
||||||
|
<Flex direction="row" justify="between" align="center">
|
||||||
|
<Flex direction="row" align="center" gap="3">
|
||||||
|
<Avatar
|
||||||
|
size="3"
|
||||||
|
src={`https://gravatar.com/avatar/${sha256sum(aliasPreview!.alias)}?d=${GRAVATAR_DEFAULT}`}
|
||||||
|
fallback={"@"}
|
||||||
|
/>
|
||||||
|
<Flex direction="column" gap="0">
|
||||||
|
<Text size="3" weight="medium">Alias created</Text>
|
||||||
|
<Text size="2" weight="light">{aliasPreview!.alias}</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<IconButton onClick={() => navigator.clipboard.writeText(aliasPreview!.alias)} variant="surface" size="2">
|
||||||
|
<CopyIcon size="16" />
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
<Flex gap="3" mt="4" justify="end">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button variant="outline">Close</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
}
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text weight="light" size="2">Freely generate and dispose of randomized aliases.</Text>
|
<Text weight="light" size="2">Freely generate and dispose of randomized aliases.</Text>
|
||||||
|
|
||||||
|
{
|
||||||
|
aliases == null
|
||||||
|
? <GhostMessage icon={<LoadingSpinner />} header="Loading..." />
|
||||||
|
: aliases.length
|
||||||
|
? <Box className="h-52">
|
||||||
|
<ScrollArea>
|
||||||
|
<Table.Root>
|
||||||
|
<Table.Header>
|
||||||
|
<Table.Row>
|
||||||
|
<Table.ColumnHeaderCell justify='start'>Alias</Table.ColumnHeaderCell>
|
||||||
|
<Table.ColumnHeaderCell justify='end'>Manage</Table.ColumnHeaderCell>
|
||||||
|
</Table.Row>
|
||||||
|
</Table.Header>
|
||||||
|
|
||||||
|
<Table.Body>
|
||||||
|
{
|
||||||
|
aliases.map((alias => <Table.Row key={alias.id}>
|
||||||
|
<Table.Cell justify='start'>
|
||||||
|
{alias.alias}
|
||||||
|
</Table.Cell>
|
||||||
|
<Table.Cell justify='end'>
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger>
|
||||||
|
<Button variant="soft" size="1" mr="2">Delete</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
|
||||||
|
<Dialog.Content>
|
||||||
|
<Dialog.Title>Delete alias</Dialog.Title>
|
||||||
|
<Dialog.Description>
|
||||||
|
Are you sure you want to delete the alias <Code className="whitespace-nowrap">{alias.alias}</Code>?
|
||||||
|
You will not be able to recreate it afterwards.
|
||||||
|
</Dialog.Description>
|
||||||
|
|
||||||
|
<Flex gap="3" mt="4" justify="end">
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
<Dialog.Close>
|
||||||
|
<Button
|
||||||
|
variant="soft"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await deleteAlias(alias.alias);
|
||||||
|
setAliases(aliases.filter((a) => a.id != alias.id));
|
||||||
|
toast({
|
||||||
|
title: "Temporary alias deleted",
|
||||||
|
description: alias.alias,
|
||||||
|
variant: "success",
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
toast({
|
||||||
|
title: "Failed to delete alias",
|
||||||
|
description: `${e}`,
|
||||||
|
variant: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Flex>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
</Table.Cell>
|
||||||
|
</Table.Row>))
|
||||||
|
}
|
||||||
|
</Table.Body>
|
||||||
|
</Table.Root>
|
||||||
|
</ScrollArea>
|
||||||
|
</Box>
|
||||||
|
: <GhostMessage icon={<UsersRoundIcon />} header="No aliases" message="Generate an alias to begin" />
|
||||||
|
}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ export function getAllAliases() {
|
||||||
return new Promise<AliasEntry[]>(async (resolve, reject) => {
|
return new Promise<AliasEntry[]>(async (resolve, reject) => {
|
||||||
const db = database('aliases');
|
const db = database('aliases');
|
||||||
|
|
||||||
db.all("SELECT id, address, alias, pending FROM aliases", (err, res: any[]) => {
|
db.all("SELECT id, address, alias, pending, temporary FROM aliases", (err, res: any[]) => {
|
||||||
db.close();
|
db.close();
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
resolve(res.map((data) => ({
|
resolve(res.map((data) => ({
|
||||||
|
@ -99,10 +99,10 @@ export function getUserAliases(email: string, tempAliases?: boolean) {
|
||||||
const db = database('aliases');
|
const db = database('aliases');
|
||||||
|
|
||||||
db.all(
|
db.all(
|
||||||
"SELECT id, address, alias, pending FROM aliases WHERE address = ?1 " + (typeof tempAliases != 'undefined' ? "AND temporary = ?2" : ""),
|
"SELECT id, address, alias, pending, temporary FROM aliases WHERE address = ?1 " + (typeof tempAliases != 'undefined' ? "AND temporary = ?2" : ""),
|
||||||
{
|
{
|
||||||
1: email,
|
1: email,
|
||||||
2: tempAliases ? 1 : 0,
|
2: typeof tempAliases == "boolean" ? (tempAliases ? 1 : 0) : undefined,
|
||||||
},
|
},
|
||||||
(err, res: any[]) => {
|
(err, res: any[]) => {
|
||||||
db.close();
|
db.close();
|
||||||
|
@ -120,7 +120,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 alias = ?", alias, (err, res: AliasEntry) => {
|
db.get("SELECT id, address, alias, pending, temporary 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);
|
||||||
|
|
Loading…
Reference in a new issue