")
- .append($("").text(token.name))
- .append($(" | ").text(formatDateTime(token.utcDateCreated)))
- .append(
- $(" | ").append(
- $(``).on("click", () => this.renameToken(token.etapiTokenId, token.name)),
- $(``).on("click", () => this.deleteToken(token.etapiTokenId, token.name))
- )
- )
- );
- }
- }
-
- async renameToken(etapiTokenId: string, oldName: string) {
- const tokenName = await dialogService.prompt({
- title: t("etapi.rename_token_title"),
- message: t("etapi.rename_token_message"),
- defaultValue: oldName
- });
-
- if (!tokenName?.trim()) {
- return;
- }
-
- await server.patch(`etapi-tokens/${etapiTokenId}`, { name: tokenName });
-
- this.refreshTokens();
- }
-
- async deleteToken(etapiTokenId: string, name: string) {
- if (!(await dialogService.confirm(t("etapi.delete_token_confirmation", { name })))) {
- return;
- }
-
- await server.remove(`etapi-tokens/${etapiTokenId}`);
-
- this.refreshTokens();
- }
-}
diff --git a/apps/client/src/widgets/type_widgets/options/etapi.tsx b/apps/client/src/widgets/type_widgets/options/etapi.tsx
new file mode 100644
index 000000000..ebd497ccc
--- /dev/null
+++ b/apps/client/src/widgets/type_widgets/options/etapi.tsx
@@ -0,0 +1,139 @@
+import { useCallback, useEffect, useState } from "preact/hooks";
+import { t } from "../../../services/i18n";
+import Button from "../../react/Button";
+import FormText from "../../react/FormText";
+import RawHtml from "../../react/RawHtml";
+import OptionsSection from "./components/OptionsSection";
+import { EtapiToken, PostTokensResponse } from "@triliumnext/commons";
+import server from "../../../services/server";
+import toast from "../../../services/toast";
+import dialog from "../../../services/dialog";
+import { formatDateTime } from "../../../utils/formatters";
+import ActionButton from "../../react/ActionButton";
+
+type RenameTokenCallback = (tokenId: string, oldName: string) => Promise;
+type DeleteTokenCallback = (tokenId: string, name: string ) => Promise;
+
+export default function EtapiSettings() {
+ const [ tokens, setTokens ] = useState([]);
+
+ function refreshTokens() {
+ server.get("etapi-tokens").then(setTokens);
+ }
+
+ useEffect(refreshTokens, []);
+
+ const createTokenCallback = useCallback(async () => {
+ const tokenName = await dialog.prompt({
+ title: t("etapi.new_token_title"),
+ message: t("etapi.new_token_message"),
+ defaultValue: t("etapi.default_token_name")
+ });
+
+ if (!tokenName?.trim()) {
+ toast.showError(t("etapi.error_empty_name"));
+ return;
+ }
+
+ const { authToken } = await server.post("etapi-tokens", { tokenName });
+
+ await dialog.prompt({
+ title: t("etapi.token_created_title"),
+ message: t("etapi.token_created_message"),
+ defaultValue: authToken
+ });
+
+ refreshTokens();
+ }, []);
+
+ const renameTokenCallback = useCallback(async (tokenId: string, oldName: string) => {
+ const tokenName = await dialog.prompt({
+ title: t("etapi.rename_token_title"),
+ message: t("etapi.rename_token_message"),
+ defaultValue: oldName
+ });
+
+ if (!tokenName?.trim()) {
+ return;
+ }
+
+ await server.patch(`etapi-tokens/${tokenId}`, { name: tokenName });
+
+ refreshTokens();
+ }, []);
+
+ const deleteTokenCallback = useCallback(async (tokenId: string, name: string) => {
+ if (!(await dialog.confirm(t("etapi.delete_token_confirmation", { name })))) {
+ return;
+ }
+
+ await server.remove(`etapi-tokens/${tokenId}`);
+ refreshTokens();
+ }, []);
+
+ return (
+
+
+ {t("etapi.description")}
+ ${t("etapi.wiki")}`,
+ // TODO: We use window.open src/public/app/services/link.ts -> prevents regular click behavior on "a" element here because it's a relative path
+ link_to_openapi_spec: `${t("etapi.openapi_spec")}`,
+ link_to_swagger_ui: `${t("etapi.swagger_ui")}`
+ })} />
+
+
+
+
+
+ {t("etapi.existing_tokens")}
+
+
+ )
+}
+
+function TokenList({ tokens, renameCallback, deleteCallback }: { tokens: EtapiToken[], renameCallback: RenameTokenCallback, deleteCallback: DeleteTokenCallback }) {
+ if (!tokens.length) {
+ return {t("etapi.no_tokens_yet")} ;
+ }
+
+ return (
+
+
+
+
+ {t("etapi.token_name")} |
+ {t("etapi.created")} |
+ {t("etapi.actions")} |
+
+
+
+ {tokens.map(({ etapiTokenId, name, utcDateCreated}) => (
+
+ {name} |
+ {formatDateTime(utcDateCreated)} |
+
+ renameCallback(etapiTokenId!, name)}
+ />
+
+ deleteCallback(etapiTokenId!, name)}
+ />
+ |
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/client/src/widgets/type_widgets/options/sync.tsx b/apps/client/src/widgets/type_widgets/options/sync.tsx
index f312a4dfb..c19728153 100644
--- a/apps/client/src/widgets/type_widgets/options/sync.tsx
+++ b/apps/client/src/widgets/type_widgets/options/sync.tsx
@@ -86,7 +86,7 @@ export function SyncTest() {
if (result.success) {
toast.showMessage(result.message);
} else {
- toast.showError(t("sync_2.handshake_failed", { message: result.error }));
+ toast.showError(t("sync_2.handshake_failed", { message: result.message }));
}
}}
/>
diff --git a/apps/server/src/routes/api/etapi_tokens.ts b/apps/server/src/routes/api/etapi_tokens.ts
index fa1b73b17..bcb297028 100644
--- a/apps/server/src/routes/api/etapi_tokens.ts
+++ b/apps/server/src/routes/api/etapi_tokens.ts
@@ -1,16 +1,17 @@
import type { Request } from "express";
import etapiTokenService from "../../services/etapi_tokens.js";
+import { EtapiToken, PostTokensResponse } from "@triliumnext/commons";
function getTokens() {
const tokens = etapiTokenService.getTokens();
tokens.sort((a, b) => (a.utcDateCreated < b.utcDateCreated ? -1 : 1));
- return tokens;
+ return tokens satisfies EtapiToken[];
}
function createToken(req: Request) {
- return etapiTokenService.createToken(req.body.tokenName);
+ return etapiTokenService.createToken(req.body.tokenName) satisfies PostTokensResponse;
}
function patchToken(req: Request) {
diff --git a/apps/server/src/routes/api/sync.ts b/apps/server/src/routes/api/sync.ts
index 0514470ee..0e4ae2678 100644
--- a/apps/server/src/routes/api/sync.ts
+++ b/apps/server/src/routes/api/sync.ts
@@ -35,7 +35,7 @@ async function testSync(): Promise {
const [errMessage] = safeExtractMessageAndStackFromError(e);
return {
success: false,
- error: errMessage
+ message: errMessage
};
}
}
diff --git a/packages/commons/src/lib/server_api.ts b/packages/commons/src/lib/server_api.ts
index 859c5629a..d84cf18fb 100644
--- a/packages/commons/src/lib/server_api.ts
+++ b/packages/commons/src/lib/server_api.ts
@@ -1,5 +1,13 @@
import { AttributeRow, NoteType } from "./rows.js";
+type Response = {
+ success: true,
+ message: string;
+} | {
+ success: false;
+ message: string;
+}
+
export interface AppInfo {
appVersion: string;
dbVersion: number;
@@ -78,10 +86,14 @@ export interface AnonymizedDbResponse {
fileName: string;
}
-export type SyncTestResponse = {
- success: true;
- message: string;
-} | {
- success: false;
- error: string;
-};
+export type SyncTestResponse = Response;
+
+export interface EtapiToken {
+ name: string;
+ utcDateCreated: string;
+ etapiTokenId?: string;
+}
+
+export interface PostTokensResponse {
+ authToken: string;
+}
|