diff --git a/public/locales/en-US/translation.json b/public/locales/en-US/translation.json
index f67df95..0f99c43 100644
--- a/public/locales/en-US/translation.json
+++ b/public/locales/en-US/translation.json
@@ -16,7 +16,8 @@
"loading": "Loading...",
"actionNotUndoable": "This action cannot be undone!",
"copyError": "Copying to clipboard did not work. Please copy the text manually.",
- "experimentalFeature": "This is an experimental feature."
+ "experimentalFeature": "This is an experimental feature.",
+ "deletedSuccessfully": "Deleted successfully!"
},
"routes": {
@@ -202,6 +203,13 @@
}
}
}
+ },
+ "actions": {
+ "delete": {
+ "label": "Delete Alias",
+ "description": "Are you sure you want to delete this alias?",
+ "continueAction": "Delete Alias"
+ }
}
},
"ReportsRoute": {
@@ -383,6 +391,10 @@
"allowStatistics": {
"label": "Allow statistics",
"description": "If enabled, your instance will collect anonymous statistics and share them. They will only be stored locally on this instance but made public."
+ },
+ "allowAliasDeletion": {
+ "label": "Allow alias deletion",
+ "description": "If enabled, users will be able to delete their aliases."
}
}
},
@@ -395,7 +407,25 @@
"helperText": "This is just a preview. Those are not real aliases."
},
"randomAliasesIncreaseExplanation": "Random aliases' length will be increased from {{originalLength}} to {{increasedLength}} characters after {{amount}} aliases have been created.",
- "resetLabel": "Reset to defaults"
+ "resetLabel": "Reset to defaults",
+ "disabled": {
+ "title": "Global settings are disabled",
+ "description": "Global settings have been disabled. You can enable them in the configuration file."
+ }
+ },
+ "reservedAlias": {
+ "actions": {
+ "delete": {
+ "label": "Delete Reserved Alias",
+ "description": "Are you sure you want to delete this reserved alias?",
+ "continueAction": "Delete Reserved Alias"
+ }
+ }
+ },
+ "serverStatus": {
+ "noRecentReports": "There seems to be some issues with your server. The server hasn't done its cleanup in the last few days. The last report was on {{date}}.",
+ "error": "There was an error during the last server cleanup job from {{relativeDescription}}. Please check the logs for more information.",
+ "success": "Everything okay with your server! The last cleanup job was {{relativeDescription}}."
}
}
},
@@ -501,7 +531,8 @@
"notesUpdated": "Updated & encrypted notes successfully!",
"aliasChangedToEnabled": "Alias has been enabled",
"aliasChangedToDisabled": "Alias has been disabled",
- "addressCopiedToClipboard": "Address has been copied to your clipboard!"
+ "addressCopiedToClipboard": "Address has been copied to your clipboard!",
+ "aliasDeleted": "Alias has been deleted!"
}
},
"settings": {
diff --git a/src/App.tsx b/src/App.tsx
index 75f1d89..83d9842 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -81,7 +81,8 @@ const router = createBrowserRouter([
element: ,
},
{
- path: "/aliases/:addressInBase64",
+ path: "/aliases/:id",
+ loader: getServerSettings,
element: ,
},
{
@@ -105,6 +106,7 @@ const router = createBrowserRouter([
},
{
path: "/admin",
+ loader: getServerSettings,
element: ,
},
{
diff --git a/src/I18nHandler.tsx b/src/I18nHandler.tsx
index 47058d2..8237ff3 100644
--- a/src/I18nHandler.tsx
+++ b/src/I18nHandler.tsx
@@ -2,7 +2,7 @@ import * as yup from "yup"
import {useTranslation} from "react-i18next"
import {useEffect} from "react"
import {de} from "yup-locales"
-import en from "yup/es/locale"
+import en from "yup/lib/locale"
const YUP_LOCALE_LANGUAGE_MAP: Record = {
"en-US": en,
diff --git a/src/apis/delete-alias.ts b/src/apis/delete-alias.ts
new file mode 100644
index 0000000..9d24d17
--- /dev/null
+++ b/src/apis/delete-alias.ts
@@ -0,0 +1,10 @@
+import {client} from "~/constants/axios-client"
+import {SimpleDetailResponse} from "~/server-types"
+
+export default async function deleteAlias(id: string): Promise {
+ const {data} = await client.delete(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/alias/${id}`, {
+ withCredentials: true,
+ })
+
+ return data
+}
diff --git a/src/apis/delete-reserved-alias.ts b/src/apis/delete-reserved-alias.ts
new file mode 100644
index 0000000..f3b019b
--- /dev/null
+++ b/src/apis/delete-reserved-alias.ts
@@ -0,0 +1,13 @@
+import {client} from "~/constants/axios-client"
+import {SimpleDetailResponse} from "~/server-types"
+
+export default async function deleteReservedAlias(id: string): Promise {
+ const {data} = await client.delete(
+ `${import.meta.env.VITE_SERVER_BASE_URL}/v1/reserved-alias/${id}`,
+ {
+ withCredentials: true,
+ },
+ )
+
+ return data
+}
diff --git a/src/apis/get-admin-settings.ts b/src/apis/get-admin-settings.ts
index afa9427..8754f03 100644
--- a/src/apis/get-admin-settings.ts
+++ b/src/apis/get-admin-settings.ts
@@ -1,7 +1,13 @@
import {client} from "~/constants/axios-client"
import {AdminSettings} from "~/server-types"
-export default async function getAdminSettings(): Promise> {
+export type GetAdminSettingsResponse =
+ | Partial & {
+ detail: string
+ code: "error:settings:global_settings_disabled"
+ }
+
+export default async function getAdminSettings(): Promise {
const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/admin/settings`, {
withCredentials: true,
})
diff --git a/src/apis/get-alias.ts b/src/apis/get-alias.ts
index 775af6e..00fdee9 100644
--- a/src/apis/get-alias.ts
+++ b/src/apis/get-alias.ts
@@ -1,8 +1,8 @@
import {client} from "~/constants/axios-client"
import {Alias} from "~/server-types"
-export default async function getAlias(address: string): Promise {
- const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/alias/${address}`, {
+export default async function getAlias(aliasID: string): Promise {
+ const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/alias/${aliasID}`, {
withCredentials: true,
})
diff --git a/src/apis/get-latest-cron-report.ts b/src/apis/get-latest-cron-report.ts
new file mode 100644
index 0000000..17e8786
--- /dev/null
+++ b/src/apis/get-latest-cron-report.ts
@@ -0,0 +1,13 @@
+import {ServerCronReport} from "~/server-types"
+import {client} from "~/constants/axios-client"
+
+export default async function getLatestCronReport(): Promise {
+ const {data} = await client.get(
+ `${import.meta.env.VITE_SERVER_BASE_URL}/v1/admin/cron-report/latest/`,
+ {
+ withCredentials: true,
+ },
+ )
+
+ return data
+}
diff --git a/src/apis/get-me.ts b/src/apis/get-me.ts
index c211f00..bee5c36 100644
--- a/src/apis/get-me.ts
+++ b/src/apis/get-me.ts
@@ -1,7 +1,7 @@
-import {AuthenticationDetails} from "~/server-types"
+import {ServerUser} from "~/server-types"
import {client} from "~/constants/axios-client"
-export default async function getMe(): Promise {
+export default async function getMe(): Promise {
const {data} = await client.get(`${import.meta.env.VITE_SERVER_BASE_URL}/v1/account/me`, {
withCredentials: true,
})
diff --git a/src/apis/helpers/decrypt-cron-report-data.ts b/src/apis/helpers/decrypt-cron-report-data.ts
new file mode 100644
index 0000000..d03c0fc
--- /dev/null
+++ b/src/apis/helpers/decrypt-cron-report-data.ts
@@ -0,0 +1,33 @@
+import camelcaseKeys from "camelcase-keys"
+import update from "immutability-helper"
+
+import {AuthContextType} from "~/components"
+import {CronReport} from "~/server-types"
+import {extractCleartextFromSignedMessage} from "~/utils"
+
+export default async function decryptCronReportData(
+ signedMessage: string,
+ decryptContent: AuthContextType["_decryptUsingPrivateKey"],
+ publicKeyInPEM: string,
+): Promise {
+ const encryptedMessage = await extractCleartextFromSignedMessage(signedMessage, publicKeyInPEM)
+
+ return update(
+ camelcaseKeys(
+ JSON.parse(await decryptContent(encryptedMessage)) as CronReport["reportData"],
+ {
+ deep: true,
+ },
+ ),
+ {
+ report: {
+ startedAt: {
+ $apply: startedAt => new Date(startedAt),
+ },
+ finishedAt: {
+ $apply: finishedAt => new Date(finishedAt),
+ },
+ },
+ },
+ )
+}
diff --git a/src/apis/index.ts b/src/apis/index.ts
index bfb40f1..fbb1e32 100644
--- a/src/apis/index.ts
+++ b/src/apis/index.ts
@@ -52,3 +52,9 @@ export * from "./get-admin-settings"
export {default as getAdminSettings} from "./get-admin-settings"
export * from "./update-admin-settings"
export {default as updateAdminSettings} from "./update-admin-settings"
+export * from "./delete-alias"
+export {default as deleteAlias} from "./delete-alias"
+export * from "./delete-reserved-alias"
+export {default as deleteReservedAlias} from "./delete-reserved-alias"
+export * from "./get-latest-cron-report"
+export {default as getLatestCronReport} from "./get-latest-cron-report"
diff --git a/src/apis/refresh-token.ts b/src/apis/refresh-token.ts
index 8d05f55..55b5d09 100644
--- a/src/apis/refresh-token.ts
+++ b/src/apis/refresh-token.ts
@@ -1,14 +1,9 @@
import {ServerUser} from "~/server-types"
import {client} from "~/constants/axios-client"
-export interface RefreshTokenResult {
- user: ServerUser
- detail: string
-}
-
export const REFRESH_TOKEN_URL = `${import.meta.env.VITE_SERVER_BASE_URL}/v1/auth/refresh`
-export default async function refreshToken(): Promise {
+export default async function refreshToken(): Promise {
const {data} = await client.post(
REFRESH_TOKEN_URL,
{},
diff --git a/src/apis/resend-email-login-code.ts b/src/apis/resend-email-login-code.ts
index 97381ff..28ff7f3 100644
--- a/src/apis/resend-email-login-code.ts
+++ b/src/apis/resend-email-login-code.ts
@@ -1,5 +1,5 @@
-import {SimpleDetailResponse} from "~/server-types"
import {client} from "~/constants/axios-client"
+import {SimpleDetailResponse} from "~/server-types"
export interface ResendEmailLoginCodeData {
email: string
diff --git a/src/apis/resend-email-verification-code.ts b/src/apis/resend-email-verification-code.ts
index 67f90ae..a46cc4d 100644
--- a/src/apis/resend-email-verification-code.ts
+++ b/src/apis/resend-email-verification-code.ts
@@ -1,9 +1,15 @@
import {SimpleDetailResponse} from "~/server-types"
import {client} from "~/constants/axios-client"
+export type ResendEmailVerificationCodeResponse =
+ | SimpleDetailResponse & {
+ detail: string
+ code: "ok:email_already_verified"
+ }
+
export default async function resendEmailVerificationCode(
email: string,
-): Promise {
+): Promise {
const {data} = await client.post(
`${import.meta.env.VITE_SERVER_BASE_URL}/v1/auth/resend-email`,
{
diff --git a/src/apis/update-account.ts b/src/apis/update-account.ts
index 5961783..efb5d2a 100644
--- a/src/apis/update-account.ts
+++ b/src/apis/update-account.ts
@@ -1,4 +1,4 @@
-import {AuthenticationDetails, Language} from "~/server-types"
+import {Language, ServerUser} from "~/server-types"
import {client} from "~/constants/axios-client"
import parseUser from "~/apis/helpers/parse-user"
@@ -9,10 +9,8 @@ export interface UpdateAccountData {
language?: Language
}
-export default async function updateAccount(
- updateData: UpdateAccountData,
-): Promise {
- const {data} = await client.patch(
+export default async function updateAccount(updateData: UpdateAccountData): Promise {
+ const {data: user} = await client.patch(
`${import.meta.env.VITE_SERVER_BASE_URL}/v1/account`,
updateData,
{
@@ -20,8 +18,5 @@ export default async function updateAccount(
},
)
- return {
- ...data,
- user: parseUser(data.user),
- }
+ return parseUser(user)
}
diff --git a/src/apis/update-admin-settings.ts b/src/apis/update-admin-settings.ts
index 5be9fd9..e92d99d 100644
--- a/src/apis/update-admin-settings.ts
+++ b/src/apis/update-admin-settings.ts
@@ -1,9 +1,15 @@
import {client} from "~/constants/axios-client"
import {AdminSettings} from "~/server-types"
+export type UpdateAdminSettingsResponse =
+ | Partial & {
+ detail: string
+ code: "error:settings:global_settings_disabled"
+ }
+
export default async function updateAdminSettings(
settings: Partial,
-): Promise {
+): Promise {
const {data} = await client.patch(
`${import.meta.env.VITE_SERVER_BASE_URL}/v1/admin/settings`,
settings,
diff --git a/src/apis/verify-email.ts b/src/apis/verify-email.ts
index 89ca3ad..65e84a0 100644
--- a/src/apis/verify-email.ts
+++ b/src/apis/verify-email.ts
@@ -1,4 +1,4 @@
-import {AuthenticationDetails} from "~/server-types"
+import {ServerUser} from "~/server-types"
import {client} from "~/constants/axios-client"
import parseUser from "~/apis/helpers/parse-user"
@@ -7,11 +7,8 @@ export interface VerifyEmailData {
token: string
}
-export default async function verifyEmail({
- email,
- token,
-}: VerifyEmailData): Promise {
- const {data} = await client.post(
+export default async function verifyEmail({email, token}: VerifyEmailData): Promise {
+ const {data: user} = await client.post(
`${import.meta.env.VITE_SERVER_BASE_URL}/v1/auth/verify-email`,
{
email: email,
@@ -22,8 +19,5 @@ export default async function verifyEmail({
},
)
- return {
- ...data,
- user: parseUser(data.user),
- }
+ return parseUser(user)
}
diff --git a/src/apis/verify-login-with-email.ts b/src/apis/verify-login-with-email.ts
index 229204e..9ae6004 100644
--- a/src/apis/verify-login-with-email.ts
+++ b/src/apis/verify-login-with-email.ts
@@ -1,4 +1,4 @@
-import {AuthenticationDetails} from "~/server-types"
+import {ServerUser} from "~/server-types"
import {client} from "~/constants/axios-client"
import parseUser from "~/apis/helpers/parse-user"
@@ -12,8 +12,8 @@ export default async function verifyLoginWithEmail({
email,
token,
sameRequestToken,
-}: VerifyLoginWithEmailData): Promise {
- const {data} = await client.post(
+}: VerifyLoginWithEmailData): Promise {
+ const {data: user} = await client.post(
`${import.meta.env.VITE_SERVER_BASE_URL}/v1/auth/login/email-token/verify`,
{
email,
@@ -25,8 +25,5 @@ export default async function verifyLoginWithEmail({
},
)
- return {
- ...data,
- user: parseUser(data.user),
- }
+ return parseUser(user)
}
diff --git a/src/components/AuthContext/use-user.ts b/src/components/AuthContext/use-user.ts
index b9e44c4..988cc82 100644
--- a/src/components/AuthContext/use-user.ts
+++ b/src/components/AuthContext/use-user.ts
@@ -3,8 +3,8 @@ import {AxiosError} from "axios"
import {useMutation, useQuery} from "@tanstack/react-query"
-import {REFRESH_TOKEN_URL, RefreshTokenResult, getMe, refreshToken} from "~/apis"
-import {AuthenticationDetails, ServerUser, User} from "~/server-types"
+import {REFRESH_TOKEN_URL, getMe, refreshToken} from "~/apis"
+import {ServerUser, User} from "~/server-types"
import {client} from "~/constants/axios-client"
export interface UseAuthData {
@@ -22,11 +22,11 @@ export default function useUser({
user,
updateUser,
}: UseAuthData) {
- const {mutateAsync: refresh} = useMutation(refreshToken, {
- onError: () => logout(),
+ const {mutateAsync: refresh} = useMutation(refreshToken, {
+ onError: logout,
})
- useQuery(["get_me"], getMe, {
+ useQuery(["get_me"], getMe, {
refetchOnWindowFocus: "always",
refetchOnReconnect: "always",
retry: 2,
diff --git a/src/route-widgets/ReportDetailRoute/DeleteButton.tsx b/src/components/widgets/DeleteAPIButton.tsx
similarity index 66%
rename from src/route-widgets/ReportDetailRoute/DeleteButton.tsx
rename to src/components/widgets/DeleteAPIButton.tsx
index 0cf44a2..9ea0038 100644
--- a/src/route-widgets/ReportDetailRoute/DeleteButton.tsx
+++ b/src/components/widgets/DeleteAPIButton.tsx
@@ -15,24 +15,35 @@ import {
} from "@mui/material"
import {useMutation} from "@tanstack/react-query"
-import {deleteReport} from "~/apis"
import {useErrorSuccessSnacks} from "~/hooks"
-import {SimpleDetailResponse} from "~/server-types"
-export interface DeleteButtonProps {
- id: string
+export interface DeleteAPIButtonProps {
+ onDelete: () => Promise
+ label: string
+ continueLabel?: string
+
+ description?: string
+ successMessage?: string
+ navigateTo?: string
}
-export default function ReportDetailRoute({id}: DeleteButtonProps): ReactElement {
+export default function DeleteAPIButton({
+ onDelete,
+ successMessage,
+ label,
+ continueLabel,
+ description,
+ navigateTo = "/aliases",
+}: DeleteAPIButtonProps): ReactElement {
const {t} = useTranslation()
const {showError, showSuccess} = useErrorSuccessSnacks()
const navigate = useNavigate()
- const {mutate} = useMutation(() => deleteReport(id), {
+ const {mutate} = useMutation(onDelete, {
onError: showError,
onSuccess: () => {
- showSuccess(t("relations.report.mutations.success.reportDeleted"))
- navigate("/reports")
+ showSuccess(successMessage || t("general.deletedSuccessfully"))
+ navigate(navigateTo)
},
})
@@ -47,14 +58,12 @@ export default function ReportDetailRoute({id}: DeleteButtonProps): ReactElement
startIcon={}
onClick={() => setShowDeleteDialog(true)}
>
- {t("routes.ReportDetailRoute.actions.delete.label")}
+ {label}
diff --git a/src/components/widgets/QueryResult.tsx b/src/components/widgets/QueryResult.tsx
index b58593f..402b064 100644
--- a/src/components/widgets/QueryResult.tsx
+++ b/src/components/widgets/QueryResult.tsx
@@ -17,7 +17,7 @@ export default function QueryResult({
query,
children: render,
}: QueryResultProps): ReactElement {
- if (query.data) {
+ if (query.data !== undefined) {
return render(query.data)
}
diff --git a/src/components/widgets/index.ts b/src/components/widgets/index.ts
index d25c848..7ff371e 100644
--- a/src/components/widgets/index.ts
+++ b/src/components/widgets/index.ts
@@ -45,6 +45,8 @@ export {default as LoadingData} from "./LoadingData"
export * from "./ExternalLinkIndication"
export {default as ExternalLinkIndication} from "./ExternalLinkIndication"
export {default as ExtensionSignalHandler} from "./ExtensionalSignalHandler"
+export * from "./DeleteAPIButton"
+export {default as DeleteButton} from "./DeleteAPIButton"
export * from "./StringPoolField"
export * as SimplePageBuilder from "./simple-page-builder"
diff --git a/src/constants/admin-settings.ts b/src/constants/admin-settings.ts
index 5d340a0..b85bf6b 100644
--- a/src/constants/admin-settings.ts
+++ b/src/constants/admin-settings.ts
@@ -11,4 +11,5 @@ export const DEFAULT_ADMIN_SETTINGS: AdminSettings = {
imageProxyStorageLifeTimeInHours: 24,
enableImageProxy: true,
allowStatistics: true,
+ allowAliasDeletion: false,
}
diff --git a/src/hooks/use-error-success-snacks.ts b/src/hooks/use-error-success-snacks.ts
index 9640ed4..009b244 100644
--- a/src/hooks/use-error-success-snacks.ts
+++ b/src/hooks/use-error-success-snacks.ts
@@ -28,17 +28,18 @@ export default function useErrorSuccessSnacks(): UseErrorSuccessSnacksResult {
})
}
const showError = (error: Error) => {
- const parsedError = parseFastAPIError(error as AxiosError)
+ let message
- if ("detail" in parsedError) {
- $errorSnackbarKey.current = enqueueSnackbar(
- parsedError.detail || t("general.defaultError"),
- {
- variant: "error",
- autoHideDuration: ERROR_SNACKBAR_SHOW_DURATION,
- },
- )
- }
+ try {
+ const parsedError = parseFastAPIError(error as AxiosError)
+
+ message = parsedError.detail
+ } catch (e) {}
+
+ $errorSnackbarKey.current = enqueueSnackbar(message || t("general.defaultError"), {
+ variant: "error",
+ autoHideDuration: ERROR_SNACKBAR_SHOW_DURATION,
+ })
}
return {
diff --git a/src/route-widgets/AdminPage/ReservedAliasesList.tsx b/src/route-widgets/AdminPage/ReservedAliasesList.tsx
deleted file mode 100644
index b856845..0000000
--- a/src/route-widgets/AdminPage/ReservedAliasesList.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import {ReactElement} from "react"
-import {AxiosError} from "axios"
-
-import {useQuery} from "@tanstack/react-query"
-import {List, ListItem, ListItemText} from "@mui/material"
-
-import {getReservedAliases} from "~/apis"
-import {PaginationResult, ReservedAlias} from "~/server-types"
-import {QueryResult} from "~/components"
-
-export interface ReservedAliasesListProps {}
-
-export default function ReservedAliasesList({}: ReservedAliasesListProps): ReactElement {
- const query = useQuery, AxiosError>(
- ["getReservedAliases"],
- () => getReservedAliases(),
- )
-
- return (
- , AxiosError> query={query}>
- {({items}) => (
-
- {items.map(alias => (
-
-
-
- ))}
-
- )}
-
- )
-}
diff --git a/src/route-widgets/AdminRoute/ServerStatus.tsx b/src/route-widgets/AdminRoute/ServerStatus.tsx
new file mode 100644
index 0000000..8ed9c7f
--- /dev/null
+++ b/src/route-widgets/AdminRoute/ServerStatus.tsx
@@ -0,0 +1,77 @@
+import {ReactElement, useContext} from "react"
+import {AxiosError} from "axios"
+import {useLoaderData} from "react-router-dom"
+import {useTranslation} from "react-i18next"
+import format from "date-fns/format"
+import subDays from "date-fns/subDays"
+
+import {useQuery} from "@tanstack/react-query"
+import {Alert} from "@mui/material"
+
+import {CronReport, ServerSettings} from "~/server-types"
+import {getLatestCronReport} from "~/apis"
+import {AuthContext, QueryResult} from "~/components"
+import decryptCronReportData from "~/apis/helpers/decrypt-cron-report-data"
+import formatRelative from "date-fns/formatRelative"
+
+const MAX_REPORT_DAY_THRESHOLD = 5
+
+export default function ServerStatus(): ReactElement {
+ const serverSettings = useLoaderData() as ServerSettings
+ const {t} = useTranslation()
+ const {_decryptUsingPrivateKey} = useContext(AuthContext)
+
+ const query = useQuery(["get_latest_cron_report"], async () => {
+ const encryptedReport = await getLatestCronReport()
+
+ ;(encryptedReport as any as CronReport).reportData = await decryptCronReportData(
+ encryptedReport.reportData.encryptedReport,
+ _decryptUsingPrivateKey,
+ serverSettings.publicKey,
+ )
+
+ return encryptedReport as any as CronReport
+ })
+
+ return (
+ query={query}>
+ {report => {
+ const thresholdDate = subDays(new Date(), MAX_REPORT_DAY_THRESHOLD)
+
+ if (report.createdAt < thresholdDate) {
+ return (
+
+ {t("routes.AdminRoute.serverStatus.noRecentReports", {
+ date: format(new Date(report.createdAt), "Pp"),
+ })}
+
+ )
+ }
+
+ if (report.reportData.report.status === "error") {
+ return (
+
+ {t("routes.AdminRoute.serverStatus.error", {
+ relativeDescription: formatRelative(
+ new Date(report.createdAt),
+ new Date(),
+ ),
+ })}
+
+ )
+ }
+
+ return (
+
+ {t("routes.AdminRoute.serverStatus.success", {
+ relativeDescription: formatRelative(
+ new Date(report.createdAt),
+ new Date(),
+ ),
+ })}
+
+ )
+ }}
+
+ )
+}
diff --git a/src/route-widgets/AliasesRoute/AliasesListItem.tsx b/src/route-widgets/AliasesRoute/AliasesListItem.tsx
index a88fcb4..6693421 100644
--- a/src/route-widgets/AliasesRoute/AliasesListItem.tsx
+++ b/src/route-widgets/AliasesRoute/AliasesListItem.tsx
@@ -29,7 +29,7 @@ export default function AliasesListItem({
// @ts-ignore
component={isInCopyAddressMode ? undefined : RouterLink}
key={alias.id}
- to={isInCopyAddressMode ? undefined : `/aliases/${btoa(address)}`}
+ to={isInCopyAddressMode ? undefined : `/aliases/${alias.id}`}
onClick={(event: any) => {
if (isInCopyAddressMode) {
event.preventDefault()
diff --git a/src/route-widgets/CompleteAccountRoute/PasswordForm.tsx b/src/route-widgets/CompleteAccountRoute/PasswordForm.tsx
index e8dcf11..6c90a8a 100644
--- a/src/route-widgets/CompleteAccountRoute/PasswordForm.tsx
+++ b/src/route-widgets/CompleteAccountRoute/PasswordForm.tsx
@@ -13,7 +13,7 @@ import {useMutation} from "@tanstack/react-query"
import {AuthContext, PasswordField, SimpleForm} from "~/components"
import {setupEncryptionForUser} from "~/utils"
import {useExtensionHandler, useNavigateToNext, useSystemPreferredTheme, useUser} from "~/hooks"
-import {AuthenticationDetails, ServerSettings} from "~/server-types"
+import {ServerSettings, ServerUser} from "~/server-types"
import {UpdateAccountData, updateAccount} from "~/apis"
export interface PasswordFormProps {
@@ -51,9 +51,7 @@ export default function PasswordForm({onDone}: PasswordFormProps): ReactElement
const {_setEncryptionPassword, login} = useContext(AuthContext)
- const {mutateAsync} = useMutation(
- updateAccount,
- )
+ const {mutateAsync} = useMutation(updateAccount)
const formik = useFormik