commit 5ad8afc229a748d1428633035ce63b47354519ce
parent e3c6913eb9c899f197f48e55b39c8e01b8afbbbe
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Tue, 20 Oct 2026 12:54:26 +0200
taldir/maildir: implement progress tokens in API
Diffstat:
7 files changed, 181 insertions(+), 19 deletions(-)
diff --git a/packages/taler-util/src/http-client/mailbox.ts b/packages/taler-util/src/http-client/mailbox.ts
@@ -21,7 +21,7 @@ import {
LibtoolVersion,
MailboxConfiguration,
MailboxMetadata,
- MailboxRegisterRequest,
+ MailboxRegisterRequestHttp,
MailboxRegisterResult,
OperationAlternative,
OperationFail,
@@ -295,7 +295,7 @@ export class TalerMailboxInstanceHttpClient {
* https://docs.taler.net/core/api-mailbox.html#post--register
*/
async registerMailbox(
- req: MailboxRegisterRequest,
+ req: MailboxRegisterRequestHttp,
): Promise<
| OperationOk<MailboxRegisterResult>
| OperationFail<HttpStatusCode.Forbidden>
diff --git a/packages/taler-util/src/types-taler-mailbox.ts b/packages/taler-util/src/types-taler-mailbox.ts
@@ -91,7 +91,7 @@ export interface MailboxRegisterPaymentRequired {
talerUri?: string;
}
-export interface MailboxRegisterRequest {
+export interface MailboxRegisterRequestHttp {
// Mailbox metadata.
mailbox_metadata: MailboxMetadata;
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -1645,6 +1645,7 @@ export interface TaldirRegistrationCompletionRequest {
challenge: string;
taldirBaseUrl: string;
targetUri: string;
+ progressToken?: string;
}
export const codecForTaldirRegistrationCompletionRequest =
@@ -1655,6 +1656,7 @@ export const codecForTaldirRegistrationCompletionRequest =
.property("alias", codecForString())
.property("aliasType", codecForString())
.property("challenge", codecForString())
+ .property("progressToken", codecOptional(codecForString()))
.build("TaldirRegistrationCompletionRequest");
export interface TaldirRegistrationRequest {
@@ -1663,6 +1665,7 @@ export interface TaldirRegistrationRequest {
targetUri: string;
taldirBaseUrl: string;
duration: RelativeTime;
+ progressToken?: string;
}
export const codecForTaldirRegistrationRequest =
@@ -1673,12 +1676,14 @@ export const codecForTaldirRegistrationRequest =
.property("alias", codecForString())
.property("aliasType", codecForString())
.property("duration", codecForDuration)
+ .property("progressToken", codecOptional(codecForString()))
.build("TaldirRegistrationRequest");
export interface TaldirLookupRequest {
alias: string;
aliasType: string;
taldirBaseUrl: string;
+ progressToken?: string;
}
export const codecForTaldirLookupRequest = (): Codec<TaldirLookupRequest> =>
@@ -1686,6 +1691,7 @@ export const codecForTaldirLookupRequest = (): Codec<TaldirLookupRequest> =>
.property("taldirBaseUrl", codecForString())
.property("alias", codecForString())
.property("aliasType", codecForString())
+ .property("progressToken", codecOptional(codecForString()))
.build("TaldirLookupRequest");
export interface TaldirLookupResponse {
@@ -1698,11 +1704,13 @@ export interface TaldirSupportedAliasesResponse {
export interface TaldirGetSupportedAliasesRequest {
taldirBaseUrl: string;
+ progressToken?: string;
}
export const codecForTaldirGetSupportedAliasesRequest = (): Codec<TaldirGetSupportedAliasesRequest> =>
buildCodecForObject<TaldirGetSupportedAliasesRequest>()
.property("taldirBaseUrl", codecForString())
+ .property("progressToken", codecOptional(codecForString()))
.build("TaldirGetSupportedAliasesRequest");
export interface GetMailboxResponse {
@@ -1718,7 +1726,7 @@ export interface MailboxBaseUrl {
}
export const codecForMailboxBaseUrl = (): Codec<MailboxBaseUrl> =>
- buildCodecForObject<MailboxBaseUrl>()
+buildCodecForObject<MailboxBaseUrl>()
.property("mailboxBaseUrl", codecForString())
.build("MailboxBaseUrl");
@@ -1743,8 +1751,46 @@ export const codecForMailboxConfiguration = (): Codec<MailboxConfiguration> =>
export interface SendTalerUriMailboxMessageRequest {
contact: ContactEntry;
talerUri: string;
+ progressToken?: string;
+}
+
+export interface MailboxRefreshRequest {
+ mailboxConf: MailboxConfiguration;
+ progressToken?: string;
+}
+
+export const codecForMailboxRefreshRequest =
+ (): Codec<MailboxRefreshRequest> =>
+ buildCodecForObject<MailboxRefreshRequest>()
+ .property("mailboxConf", codecForMailboxConfiguration())
+ .property("progressToken", codecOptional(codecForString()))
+ .build("MailboxRefreshRequest");
+
+export interface MailboxRegistrationRequest {
+ mailboxConf: MailboxConfiguration;
+ progressToken?: string;
}
+export const codecForMailboxRegistrationRequest =
+ (): Codec<MailboxRegistrationRequest> =>
+ buildCodecForObject<MailboxRegistrationRequest>()
+ .property("mailboxConf", codecForMailboxConfiguration())
+ .property("progressToken", codecOptional(codecForString()))
+ .build("MailboxRegistrationRequest");
+
+export interface MailboxInitializationRequest {
+ mailboxBaseUrl: MailboxBaseUrl;
+ progressToken?: string;
+}
+
+export const codecForMailboxInitializationRequest =
+ (): Codec<MailboxInitializationRequest> =>
+ buildCodecForObject<MailboxInitializationRequest>()
+ .property("mailboxBaseUrl", codecForMailboxBaseUrl())
+ .property("progressToken", codecOptional(codecForString()))
+ .build("MailboxInitializationRequest");
+
+
export interface AddMailboxMessageRequest {
message: MailboxMessageRecord;
}
@@ -1794,6 +1840,7 @@ export const codecForSendTalerUriMailboxMessageRequest =
buildCodecForObject<SendTalerUriMailboxMessageRequest>()
.property("contact", codecForContactEntry())
.property("talerUri", codecForString())
+ .property("progressToken", codecOptional(codecForString()))
.build("SendTalerUriMailboxMessageRequest");
export interface WalletCoreVersion {
diff --git a/packages/taler-wallet-core/src/mailbox.ts b/packages/taler-wallet-core/src/mailbox.ts
@@ -35,7 +35,8 @@ import {
MailboxMessageRecordsResponse,
MailboxMessagesResponse,
MailboxMetadata,
- MailboxRegisterRequest,
+ MailboxRefreshRequest,
+ MailboxRegistrationRequest,
MailboxRegisterResult,
NotificationType,
Result,
@@ -58,8 +59,14 @@ import {
sha512,
stringToBytes,
succeedOrThrow,
+ MailboxRegisterRequestHttp,
+ MailboxInitializationRequest,
} from "@gnu-taler/taler-util";
import { WalletExecutionContext } from "./wallet.js";
+import {
+ runWithMaybeProgressContext,
+} from "./progress.js";
+
const logger = new Logger("mailbox.ts");
export const MAX_MAILBOX_REFRESH_BATCHES = 100;
@@ -135,8 +142,21 @@ export async function listMailboxMessages(
*/
export async function registerMailbox(
wex: WalletExecutionContext,
- mailboxConf: MailboxConfiguration,
+ req: MailboxRegistrationRequest,
+): Promise<MailboxRegisterResult> {
+ return runWithMaybeProgressContext(
+ wex,
+ "mailboxRegistrationRequest",
+ req.progressToken,
+ () => internalRegisterMailbox(wex, req),
+ );
+}
+
+async function internalRegisterMailbox(
+ wex: WalletExecutionContext,
+ req: MailboxRegistrationRequest,
): Promise<MailboxRegisterResult> {
+ const mailboxConf = req.mailboxConf;
const privateSigningKey = decodeCrock(mailboxConf.privateKey);
const signingKey = eddsaGetPublic(privateSigningKey);
const encryptionKey = hpkeSecretKeyGetPublic(
@@ -176,7 +196,7 @@ export async function registerMailbox(
encryption_key_type: "X25519", // FIXME only supported encryption key type ATM
expiration: mailboxConf.expiration,
};
- const req: MailboxRegisterRequest = {
+ const httpReq: MailboxRegisterRequestHttp = {
mailbox_metadata: info,
signature: encodeCrock(signature),
};
@@ -185,7 +205,7 @@ export async function registerMailbox(
wex.http,
);
- const resp = await mailboxClient.registerMailbox(req);
+ const resp = await mailboxClient.registerMailbox(httpReq);
switch (resp.case) {
case "ok":
return resp.body;
@@ -220,10 +240,23 @@ export async function getMailbox(
* Create new mailbox configuration locally and
* try to register it.
*/
-export async function createNewMailbox(
+export async function initializeMailbox(
wex: WalletExecutionContext,
- mailboxBaseUrl: MailboxBaseUrl,
+ req: MailboxInitializationRequest,
+): Promise<MailboxConfiguration> {
+ return runWithMaybeProgressContext(
+ wex,
+ "mailboxInitializationRequest",
+ req.progressToken,
+ () => internalInitializeMailbox(wex, req),
+ );
+}
+
+async function internalInitializeMailbox(
+ wex: WalletExecutionContext,
+ req: MailboxInitializationRequest,
): Promise<MailboxConfiguration> {
+ const mailboxBaseUrl = req.mailboxBaseUrl;
const keys = createEddsaKeyPair();
const hpkeKey = encodeCrock(hpkeCreateSecretKey());
const privKey = encodeCrock(keys.eddsaPriv);
@@ -242,7 +275,10 @@ export async function createNewMailbox(
expiration: AbsoluteTime.toProtocolTimestamp(nowInAYear),
};
- const resp = await registerMailbox(wex, mailboxConf);
+ const resp = await registerMailbox(wex, {
+ mailboxConf: mailboxConf,
+ progressToken: req.progressToken,
+ });
if (resp.status == "payment-required") {
if (!resp.talerUri) {
throw TalerError.fromDetail(
@@ -294,8 +330,21 @@ function decryptTalerUriMessage(
*/
export async function refreshMailbox(
wex: WalletExecutionContext,
- mailboxConf: MailboxConfiguration,
+ req: MailboxRefreshRequest,
+): Promise<MailboxMessageRecordsResponse> {
+ return runWithMaybeProgressContext(
+ wex,
+ "mailboxRefreshRequest",
+ req.progressToken,
+ () => internalRefreshMailbox(wex, req),
+ );
+}
+
+async function internalRefreshMailbox(
+ wex: WalletExecutionContext,
+ req: MailboxRefreshRequest,
): Promise<MailboxMessageRecordsResponse> {
+ const mailboxConf = req.mailboxConf;
const mailboxClient = new TalerMailboxInstanceHttpClient(
mailboxConf.mailboxBaseUrl,
wex.http,
@@ -452,6 +501,18 @@ export async function sendTalerUriMessage(
wex: WalletExecutionContext,
req: SendTalerUriMailboxMessageRequest,
): Promise<EmptyObject> {
+ return runWithMaybeProgressContext(
+ wex,
+ "sendTalerUriMessage",
+ req.progressToken,
+ () => internalSendTalerUriMessage(wex, req),
+ );
+}
+
+async function internalSendTalerUriMessage(
+ wex: WalletExecutionContext,
+ req: SendTalerUriMailboxMessageRequest,
+): Promise<EmptyObject> {
const mailboxClient = new TalerMailboxInstanceHttpClient(
req.contact.mailboxBaseUri,
wex.http,
diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts
@@ -231,7 +231,7 @@ import {
codecForListSubscriptionsRequest,
codecForMigrateDatabaseRequest,
codecForMailboxBaseUrl,
- codecForMailboxConfiguration,
+ codecForMailboxRefreshRequest,
codecForPrepareBankIntegratedWithdrawalRequest,
codecForPreparePayForPaivanaRequest,
codecForPreparePayRequest,
@@ -289,6 +289,7 @@ import {
setDangerousTimetravel,
setGlobalLogLevelFromString,
validateIban,
+ codecForMailboxInitializationRequest,
} from "@gnu-taler/taler-util";
import { HttpResponse } from "@gnu-taler/taler-util/http";
import { getBalanceDetail, getBalances } from "./balance.js";
@@ -349,9 +350,9 @@ import {
import { convertDepositAmount } from "./instructedAmountConversion.js";
import {
addMailboxMessage,
- createNewMailbox,
deleteMailboxMessage,
getMailbox,
+ initializeMailbox,
listMailboxMessages,
refreshMailbox,
sendTalerUriMessage,
@@ -2590,8 +2591,8 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = {
handler: getSupportedAliasTypes,
},
[WalletApiOperation.InitializeMailbox]: {
- codec: codecForMailboxBaseUrl(),
- handler: createNewMailbox,
+ codec: codecForMailboxInitializationRequest(),
+ handler: initializeMailbox,
},
[WalletApiOperation.GetMailbox]: {
codec: codecForMailboxBaseUrl(),
@@ -2614,7 +2615,7 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = {
handler: listMailboxMessages,
},
[WalletApiOperation.RefreshMailbox]: {
- codec: codecForMailboxConfiguration(),
+ codec: codecForMailboxRefreshRequest(),
handler: refreshMailbox,
},
[WalletApiOperation.TestingGetDbStats]: {
diff --git a/packages/taler-wallet-core/src/taldir.ts b/packages/taler-wallet-core/src/taldir.ts
@@ -37,6 +37,9 @@ import {
TalerErrorCode,
} from "@gnu-taler/taler-util";
import { WalletExecutionContext } from "./wallet.js";
+import {
+ runWithMaybeProgressContext,
+} from "./progress.js";
const logger = new Logger("taldir.ts");
@@ -58,6 +61,18 @@ function createHAliasBuffer(aliasType: string, alias: string): Uint8Array {
*/
export async function getSupportedAliases(
wex: WalletExecutionContext,
+ req: TaldirGetSupportedAliasesRequest,
+): Promise<TaldirSupportedAliasesResponse> {
+ return runWithMaybeProgressContext(
+ wex,
+ "getSupportedAliasesRequest",
+ req.progressToken,
+ () => internalGetSupportedAliases(wex, req),
+ );
+}
+
+async function internalGetSupportedAliases(
+ wex: WalletExecutionContext,
lookupRequest: TaldirGetSupportedAliasesRequest,
): Promise<TaldirSupportedAliasesResponse> {
const taldirClient = new TalerDirectoryInstanceHttpClient(
@@ -86,6 +101,18 @@ export async function getSupportedAliases(
*/
export async function lookupAlias(
wex: WalletExecutionContext,
+ req: TaldirLookupRequest,
+): Promise<TaldirLookupResponse> {
+ return runWithMaybeProgressContext(
+ wex,
+ "lookupAlias",
+ req.progressToken,
+ () => internalLookupAlias(wex, req),
+ );
+}
+
+async function internalLookupAlias(
+ wex: WalletExecutionContext,
lookupRequest: TaldirLookupRequest,
): Promise<TaldirLookupResponse> {
const taldirClient = new TalerDirectoryInstanceHttpClient(
@@ -145,6 +172,18 @@ export async function lookupAlias(
*/
export async function registerAlias(
wex: WalletExecutionContext,
+ req: TaldirRegistrationRequest,
+): Promise<TaldirRegistrationResponse> {
+ return runWithMaybeProgressContext(
+ wex,
+ "registerAlias",
+ req.progressToken,
+ () => internalRegisterAlias(wex, req),
+ );
+}
+
+async function internalRegisterAlias(
+ wex: WalletExecutionContext,
registerRequest: TaldirRegistrationRequest,
): Promise<TaldirRegistrationResponse> {
const taldirClient = new TalerDirectoryInstanceHttpClient(
@@ -180,6 +219,18 @@ export async function registerAlias(
*/
export async function completeAliasRegistration(
wex: WalletExecutionContext,
+ req: TaldirRegistrationCompletionRequest,
+): Promise<EmptyObject> {
+ return runWithMaybeProgressContext(
+ wex,
+ "completeAliasRegistration",
+ req.progressToken,
+ () => internalCompleteAliasRegistration(wex, req),
+ );
+}
+
+async function internalCompleteAliasRegistration(
+ wex: WalletExecutionContext,
completionRequest: TaldirRegistrationCompletionRequest,
): Promise<EmptyObject> {
const taldirClient = new TalerDirectoryInstanceHttpClient(
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -232,6 +232,8 @@ import {
WithdrawTestBalanceRequest,
WithdrawUriInfoResponse,
WithdrawalDetailsForAmount,
+ MailboxRefreshRequest,
+ MailboxInitializationRequest,
} from "@gnu-taler/taler-util";
import { PaymentBalanceDetails } from "./balance.js";
import { WithdrawTestBalanceResult } from "./testing.js";
@@ -647,7 +649,7 @@ export type GetSupportedAliasesOp = {
*/
export type RefreshMailboxOp = {
op: WalletApiOperation.RefreshMailbox;
- request: MailboxConfiguration;
+ request: MailboxRefreshRequest;
response: MailboxMessageRecordsResponse;
};
@@ -656,7 +658,7 @@ export type RefreshMailboxOp = {
*/
export type InitializeMailboxOp = {
op: WalletApiOperation.InitializeMailbox;
- request: MailboxBaseUrl;
+ request: MailboxInitializationRequest;
response: MailboxConfiguration;
};