@remit/backend 0.0.147 → 0.0.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/error.js +10 -1
- package/dist/src/handlers/account-oauth.js +1 -1
- package/dist/src/handlers/account.js +12 -10
- package/dist/src/handlers/config.js +4 -5
- package/dist/src/service/create-remit-client.d.ts +11 -4
- package/dist/src/service/create-remit-client.js +13 -29
- package/dist/src/service/shared-deps.d.ts +1 -1
- package/dist/src/service/shared-deps.js +13 -5
- package/package.json +2 -1
- package/dist/src/handlers/ensure-account-config.d.ts +0 -9
- package/dist/src/handlers/ensure-account-config.js +0 -27
package/dist/src/error.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isPublicApiError } from "@remit/data-ports/errors";
|
|
1
|
+
import { HostRefusedError, isPublicApiError } from "@remit/data-ports/errors";
|
|
2
2
|
import { logger } from "@remit/logger";
|
|
3
3
|
import { formatResponse } from "./response.js";
|
|
4
4
|
/**
|
|
@@ -35,6 +35,15 @@ export const defaultErrorCode = (statusCode) => {
|
|
|
35
35
|
return CODE_BY_STATUS[statusCode] ?? "request_refused";
|
|
36
36
|
};
|
|
37
37
|
export const handleError = async (error) => {
|
|
38
|
+
if (error instanceof HostRefusedError) {
|
|
39
|
+
logger.error({
|
|
40
|
+
error: error.message,
|
|
41
|
+
errorKind: "quota",
|
|
42
|
+
statusCode: error.statusCode,
|
|
43
|
+
stack: error.stack,
|
|
44
|
+
}, "Host refused the request");
|
|
45
|
+
return formatResponse({ code: "request_refused", message: error.message }, error.statusCode);
|
|
46
|
+
}
|
|
38
47
|
if (error instanceof Error) {
|
|
39
48
|
if ("statusCode" in error) {
|
|
40
49
|
logger.error({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { ensureAccountConfig } from "@remit/data-ports/account-config";
|
|
2
3
|
import { AccountAuthType, AccountService, ConnectionState, } from "@remit/domain-enums";
|
|
3
4
|
import { logger } from "@remit/logger";
|
|
4
5
|
import { createMailOAuthService, microsoftProviderConfig, microsoftServicesGranted, orderServices, RefreshTokenError, } from "@remit/mail-oauth-service";
|
|
@@ -8,7 +9,6 @@ import { safeJsonParse } from "../json.js";
|
|
|
8
9
|
import { rawApiResponse } from "../response.js";
|
|
9
10
|
import { triggerAccountSyncSafe } from "./account.js";
|
|
10
11
|
import { findActiveDuplicateMailbox } from "./account-guards.js";
|
|
11
|
-
import { ensureAccountConfig } from "./ensure-account-config.js";
|
|
12
12
|
const OUTLOOK_IMAP_HOST = "outlook.office365.com";
|
|
13
13
|
const ACCOUNT_SERVICES = Object.values(AccountService);
|
|
14
14
|
const isServiceList = (value) => Array.isArray(value) &&
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { ensureAccountConfig } from "@remit/data-ports/account-config";
|
|
1
2
|
import { AccountAuthType, AccountService, ConnectionState, } from "@remit/domain-enums";
|
|
2
3
|
import { logger } from "@remit/logger";
|
|
3
4
|
import { createMailOAuthService, microsoftProviderConfig, RefreshTokenError, } from "@remit/mail-oauth-service";
|
|
4
|
-
import { ownedAccount, resolveConnectionCredentials, testImapConnection,
|
|
5
|
+
import { ownedAccount, resolveConnectionCredentials, testImapConnection, } from "@remit/mailbox-service";
|
|
5
6
|
import { deserializeEncryptedPayload, serializeEncryptedPayload, } from "@remit/secrets-service";
|
|
7
|
+
import { verifySmtpConnection } from "@remit/smtp-service";
|
|
6
8
|
import { loadMsOAuth } from "../config/msoauth.js";
|
|
7
9
|
import { deleteAccount } from "../service/account-deletion.js";
|
|
8
10
|
import { fireAndForget } from "../service/fire-and-forget.js";
|
|
@@ -11,7 +13,6 @@ import { triggerAccountSync } from "../service/trigger-sync.js";
|
|
|
11
13
|
import { assertNoDuplicateMailbox, assertNotOAuthCreate, assertPasswordProvided, resolveSyncedServices, toAccountResponse, } from "./account-guards.js";
|
|
12
14
|
import { loadAccountOverrides, upsertAccountDisplayName, writeAccountComposeLanguages, writeAccountMuted, } from "./account-overrides.js";
|
|
13
15
|
import { loadSignatureForAccount, upsertAccountSignature, } from "./account-signature.js";
|
|
14
|
-
import { ensureAccountConfig } from "./ensure-account-config.js";
|
|
15
16
|
import { resolveAccountFolderAppointments } from "./folder-role-appointments.js";
|
|
16
17
|
export { assertNotOAuthCreate, assertPasswordProvided, toAccountResponse };
|
|
17
18
|
export const triggerAccountSyncSafe = async (client, accountId) => {
|
|
@@ -44,7 +45,7 @@ export const triggerAccountSyncSafe = async (client, accountId) => {
|
|
|
44
45
|
* reauth-required case is mapped to the distinct `reauth_required` error code
|
|
45
46
|
* so the UI can route the user into the OAuth re-auth flow.
|
|
46
47
|
*/
|
|
47
|
-
const testOAuthConnection = async (msOAuth, existingAccount, account, secrets) => {
|
|
48
|
+
const testOAuthConnection = async (msOAuth, existingAccount, account, secrets, mailTransports) => {
|
|
48
49
|
const { config, credentials: app } = await loadMsOAuth(msOAuth);
|
|
49
50
|
const tokenService = createMailOAuthService(microsoftProviderConfig({
|
|
50
51
|
clientId: app.clientId,
|
|
@@ -93,19 +94,20 @@ const testOAuthConnection = async (msOAuth, existingAccount, account, secrets) =
|
|
|
93
94
|
secure: existingAccount.imapTls,
|
|
94
95
|
user: existingAccount.username,
|
|
95
96
|
credentials,
|
|
96
|
-
});
|
|
97
|
+
}, mailTransports.imap(existingAccount.imapHost, existingAccount.imapTls));
|
|
97
98
|
result.imapSuccess = imapResult.success;
|
|
98
99
|
if (!imapResult.success) {
|
|
99
100
|
result.imapError = imapResult.error;
|
|
100
101
|
}
|
|
101
102
|
// OAuth accounts share the same access token for IMAP and SMTP.
|
|
102
103
|
if (existingAccount.smtpEnabled) {
|
|
103
|
-
const smtpResult = await
|
|
104
|
+
const smtpResult = await verifySmtpConnection({
|
|
104
105
|
host: existingAccount.smtpHost ?? "",
|
|
105
106
|
port: existingAccount.smtpPort ?? 587,
|
|
106
107
|
secure: existingAccount.smtpTls ?? false,
|
|
107
108
|
user: existingAccount.smtpUsername || existingAccount.username,
|
|
108
109
|
credentials,
|
|
110
|
+
getSocket: mailTransports.smtp,
|
|
109
111
|
});
|
|
110
112
|
result.smtpSuccess = smtpResult.success;
|
|
111
113
|
if (!smtpResult.success) {
|
|
@@ -181,7 +183,7 @@ export const AccountOperations = {
|
|
|
181
183
|
AccountOperations_testConnection: async (context, ctx) => {
|
|
182
184
|
const accountConfigId = ctx.identity.accountConfigId;
|
|
183
185
|
const input = context.request.requestBody;
|
|
184
|
-
const { account, secrets } = ctx.client;
|
|
186
|
+
const { account, secrets, mailTransports } = ctx.client;
|
|
185
187
|
// Resolve password - use provided password or decrypt stored password
|
|
186
188
|
let imapPassword = input.password;
|
|
187
189
|
let smtpPassword = input.smtpPassword;
|
|
@@ -190,7 +192,7 @@ export const AccountOperations = {
|
|
|
190
192
|
unwrap(ownedAccount(existingAccount, accountConfigId, "read"));
|
|
191
193
|
const authType = existingAccount.authType ?? "password";
|
|
192
194
|
if (authType === "oauthMicrosoft") {
|
|
193
|
-
return testOAuthConnection(ctx.config.msOAuth, existingAccount, account, secrets);
|
|
195
|
+
return testOAuthConnection(ctx.config.msOAuth, existingAccount, account, secrets, mailTransports);
|
|
194
196
|
}
|
|
195
197
|
if (existingAccount.passwordHash) {
|
|
196
198
|
const payload = deserializeEncryptedPayload(JSON.parse(existingAccount.passwordHash));
|
|
@@ -218,17 +220,16 @@ export const AccountOperations = {
|
|
|
218
220
|
host: input.imapHost,
|
|
219
221
|
port: input.imapPort,
|
|
220
222
|
secure: input.imapTls,
|
|
221
|
-
startTls: input.imapStartTls,
|
|
222
223
|
user: input.username,
|
|
223
224
|
credentials: { kind: "password", password: imapPassword },
|
|
224
|
-
});
|
|
225
|
+
}, mailTransports.imap(input.imapHost, input.imapTls));
|
|
225
226
|
result.imapSuccess = imapResult.success;
|
|
226
227
|
if (!imapResult.success) {
|
|
227
228
|
result.imapError = imapResult.error;
|
|
228
229
|
}
|
|
229
230
|
// Test SMTP connection if configured
|
|
230
231
|
if (input.smtpHost) {
|
|
231
|
-
const smtpResult = await
|
|
232
|
+
const smtpResult = await verifySmtpConnection({
|
|
232
233
|
host: input.smtpHost,
|
|
233
234
|
port: input.smtpPort ?? 587,
|
|
234
235
|
secure: input.smtpTls ?? false,
|
|
@@ -237,6 +238,7 @@ export const AccountOperations = {
|
|
|
237
238
|
kind: "password",
|
|
238
239
|
password: smtpPassword ?? imapPassword,
|
|
239
240
|
},
|
|
241
|
+
getSocket: mailTransports.smtp,
|
|
240
242
|
});
|
|
241
243
|
result.smtpSuccess = smtpResult.success;
|
|
242
244
|
if (!smtpResult.success) {
|
|
@@ -65,12 +65,12 @@ const toAccountConfigResponse = (config) => ({
|
|
|
65
65
|
* The POST account-creation flow will materialize the real row when the user
|
|
66
66
|
* adds their first account.
|
|
67
67
|
*/
|
|
68
|
-
const emptyConfigResponse = (accountConfigId,
|
|
68
|
+
const emptyConfigResponse = (accountConfigId, commit) => {
|
|
69
69
|
const now = Date.now();
|
|
70
70
|
return {
|
|
71
71
|
accountConfig: {
|
|
72
72
|
accountConfigId,
|
|
73
|
-
userId,
|
|
73
|
+
userId: accountConfigId,
|
|
74
74
|
state: "active",
|
|
75
75
|
createdAt: now,
|
|
76
76
|
updatedAt: now,
|
|
@@ -91,11 +91,11 @@ export const ConfigOperations = {
|
|
|
91
91
|
throw err;
|
|
92
92
|
});
|
|
93
93
|
if (!description || description.accountConfig.length === 0) {
|
|
94
|
-
return emptyConfigResponse(accountConfigId, ctx.
|
|
94
|
+
return emptyConfigResponse(accountConfigId, ctx.config.commit);
|
|
95
95
|
}
|
|
96
96
|
const accountConfig = description.accountConfig[0];
|
|
97
97
|
if (!accountConfig) {
|
|
98
|
-
return emptyConfigResponse(accountConfigId, ctx.
|
|
98
|
+
return emptyConfigResponse(accountConfigId, ctx.config.commit);
|
|
99
99
|
}
|
|
100
100
|
// Filter out deleted accounts (tombstone pattern)
|
|
101
101
|
const activeAccounts = description.account.filter((acc) => acc.deletedAt === undefined);
|
|
@@ -151,7 +151,6 @@ export const ConfigOperations = {
|
|
|
151
151
|
appointFolderRole: (configId, accountId, role, mailboxId, lastKnownPath) => writeFolderRoleAppointment(client.accountSetting, configId, accountId, role, mailboxId, lastKnownPath),
|
|
152
152
|
}, {
|
|
153
153
|
accountConfigId,
|
|
154
|
-
userId: ctx.identity.sub,
|
|
155
154
|
document: body.document,
|
|
156
155
|
mode: body.mode ?? "validate",
|
|
157
156
|
onExisting: body.onExisting ?? "abort",
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { IAccountConfigRepository, IAccountExportRequestRepository, IAccountRepository, IAccountSettingRepository, IAddressRepository, ICalendarCollectionRepository, ICalendarEventIndexRepository, ICalendarFeedTokenRepository, ICalendarObjectRepository, ICalendarStore, ICalendarSuggestionRepository, IConfigImportRepository, IEnvelopeRepository, IFilterRepository, ILabelRepository, IMailboxLockRepository, IMailboxRepository, IMailboxSpecialUseRepository, IMessageFlagPushRepository, IMessageFlagRepository, IMessageLabelRepository, IMessagePlacementMoveRepository, IMessageRepository, IOrganizeJobRequestRepository, IOutboxAttachmentRepository, IOutboxMessageRepository, IQuarantineRepository, ISenderSignerStandingRepository, ISyncWarningRepository, IThreadMessageRepository } from "@remit/data-ports";
|
|
2
|
-
import { BodySyncQueueService, BodySyncService, type FilterConfig, FlagQueueService, type
|
|
2
|
+
import { BodySyncQueueService, BodySyncService, type ConnectionScope, type FilterConfig, FlagQueueService, type ImapTransport, MailboxQueueService, MessageMoveService, OutboxAttachmentService, OutboxQueueService, SpamReportService } from "@remit/mailbox-service";
|
|
3
3
|
import { type SecretsService } from "@remit/secrets-service";
|
|
4
|
+
import type { SmtpGetSocket } from "@remit/smtp-service";
|
|
4
5
|
import type { StorageService } from "@remit/storage-service";
|
|
5
6
|
import type { QueueName, RemitClientQueues } from "./queues.js";
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export type { ConnectionScope };
|
|
8
|
+
export type ConnectionScopeFactory = (accountService: IAccountRepository, secretsService: SecretsService) => (accountId: string) => Promise<ConnectionScope>;
|
|
9
|
+
export interface MailTransports {
|
|
10
|
+
imap: (host: string, secure: boolean) => ImapTransport;
|
|
11
|
+
smtp: SmtpGetSocket;
|
|
9
12
|
}
|
|
10
13
|
export interface RemitClientCore {
|
|
11
14
|
accountConfig: IAccountConfigRepository;
|
|
@@ -41,6 +44,7 @@ export interface RemitClientCore {
|
|
|
41
44
|
flagPush: IMessageFlagPushRepository;
|
|
42
45
|
outboxAttachment: OutboxAttachmentService;
|
|
43
46
|
createConnectionScope: (accountId: string) => Promise<ConnectionScope>;
|
|
47
|
+
mailTransports: MailTransports;
|
|
44
48
|
}
|
|
45
49
|
export interface SyncQueueServices {
|
|
46
50
|
flagQueue: FlagQueueService;
|
|
@@ -95,11 +99,14 @@ export interface RemitClientRepositories {
|
|
|
95
99
|
export interface RemitClientSharedDeps {
|
|
96
100
|
storage: StorageService;
|
|
97
101
|
secrets: SecretsService;
|
|
102
|
+
connectionScope: ConnectionScopeFactory;
|
|
103
|
+
mailTransports: MailTransports;
|
|
98
104
|
queues: RemitClientQueues;
|
|
99
105
|
}
|
|
100
106
|
export interface RemitClientDeps extends RemitClientSharedDeps {
|
|
101
107
|
repositories: RemitClientRepositories;
|
|
102
108
|
}
|
|
109
|
+
export declare const passwordConnectionScope: (transport: ImapTransport) => ConnectionScopeFactory;
|
|
103
110
|
export declare const buildReadPathFilterConfig: (repositories: Pick<RemitClientRepositories, "filter" | "messageLabel" | "mailbox" | "message" | "threadMessage" | "placementMove" | "address" | "mailboxSpecialUse">, queues: Pick<RemitClientQueues, "messageManagement">) => FilterConfig;
|
|
104
111
|
export declare const createRemitCore: (deps: Omit<RemitClientDeps, "queues">) => RemitClientCore;
|
|
105
112
|
export declare const composeSyncServices: (core: RemitClientCore, queues: Pick<RemitClientQueues, "sync">) => SyncQueueServices;
|
|
@@ -1,38 +1,21 @@
|
|
|
1
1
|
import { logger } from "@remit/logger";
|
|
2
|
-
import { BodySyncQueueService, BodySyncService,
|
|
2
|
+
import { BodySyncQueueService, BodySyncService, connectionScope, FlagPushService, FlagQueueService, fifoMailboxManagement, MailboxQueueService, MessageMoveService, OutboxAttachmentService, OutboxQueueService, PlacementMoveService, SpamReportService, SyncWarningService, } from "@remit/mailbox-service";
|
|
3
3
|
import { buildFilterConfig } from "@remit/mailbox-service/filter-config";
|
|
4
4
|
import { deserializeEncryptedPayload, } from "@remit/secrets-service";
|
|
5
|
-
const
|
|
5
|
+
export const passwordConnectionScope = (transport) => (accountService, secretsService) => async (accountId) => {
|
|
6
6
|
const account = await accountService.get(accountId);
|
|
7
7
|
if (!account.passwordHash) {
|
|
8
8
|
throw new Error(`Account ${accountId} has no passwordHash — OAuth accounts cannot use this connection path`);
|
|
9
9
|
}
|
|
10
10
|
const password = await secretsService.decrypt(deserializeEncryptedPayload(JSON.parse(account.passwordHash)));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
credentials: { kind: "password", password },
|
|
20
|
-
host: account.imapHost,
|
|
21
|
-
port: account.imapPort,
|
|
22
|
-
tls: account.imapTls,
|
|
23
|
-
});
|
|
24
|
-
connection = conn;
|
|
25
|
-
connectPromise = conn.connect().then(() => conn);
|
|
26
|
-
return connectPromise;
|
|
27
|
-
};
|
|
28
|
-
const disconnect = async () => {
|
|
29
|
-
if (connection) {
|
|
30
|
-
await connection.disconnect();
|
|
31
|
-
connection = null;
|
|
32
|
-
connectPromise = null;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
return { getConnection, disconnect };
|
|
11
|
+
return connectionScope({
|
|
12
|
+
user: account.username,
|
|
13
|
+
credentials: { kind: "password", password },
|
|
14
|
+
host: account.imapHost,
|
|
15
|
+
port: account.imapPort,
|
|
16
|
+
tls: account.imapTls,
|
|
17
|
+
transport,
|
|
18
|
+
});
|
|
36
19
|
};
|
|
37
20
|
// The read-path body backfill (describeMessage / getRawMessage →
|
|
38
21
|
// fetchAndGetBody) materializes a body that was never sync-synced, and must run
|
|
@@ -62,7 +45,7 @@ export const buildReadPathFilterConfig = (repositories, queues) => {
|
|
|
62
45
|
});
|
|
63
46
|
};
|
|
64
47
|
export const createRemitCore = (deps) => {
|
|
65
|
-
const { repositories, storage, secrets } = deps;
|
|
48
|
+
const { repositories, storage, secrets, connectionScope, mailTransports } = deps;
|
|
66
49
|
return {
|
|
67
50
|
accountConfig: repositories.accountConfig,
|
|
68
51
|
account: repositories.account,
|
|
@@ -103,7 +86,8 @@ export const createRemitCore = (deps) => {
|
|
|
103
86
|
outboxAttachmentService: repositories.outboxAttachment,
|
|
104
87
|
storage,
|
|
105
88
|
}),
|
|
106
|
-
createConnectionScope:
|
|
89
|
+
createConnectionScope: connectionScope(repositories.account, secrets),
|
|
90
|
+
mailTransports,
|
|
107
91
|
};
|
|
108
92
|
};
|
|
109
93
|
export const composeSyncServices = (core, queues) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RemitClientSharedDeps } from "./create-remit-client.js";
|
|
2
2
|
import type { QueueName, RemitClientQueues } from "./queues.js";
|
|
3
3
|
export type SqsQueueUrls = Record<QueueName, string>;
|
|
4
4
|
export declare const sqsQueueUrlsFromEnv: () => SqsQueueUrls;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { nodeTransport } from "@remit/mailbox-service/node-transport";
|
|
1
2
|
import { createCachedDataKeyProvider, createSecretsService, } from "@remit/secrets-service";
|
|
2
3
|
import { createKmsDataKeyProvider, FAKE_KMS_KEY_ID, } from "@remit/secrets-service/kms";
|
|
4
|
+
import { directSmtpSocket } from "@remit/smtp-service";
|
|
3
5
|
import { createFifoQueueProducer, createStandardQueueProducer, } from "@remit/sqs-client/producer";
|
|
4
6
|
import { createStorageService } from "@remit/storage-service/s3";
|
|
5
7
|
import { env } from "expect-env";
|
|
8
|
+
import { passwordConnectionScope, } from "./create-remit-client.js";
|
|
6
9
|
export const sqsQueueUrlsFromEnv = () => ({
|
|
7
10
|
sync: env.SQS_QUEUE_URL,
|
|
8
11
|
accountFanout: env.SQS_QUEUE_URL_ACCOUNT_FANOUT,
|
|
@@ -19,8 +22,13 @@ export const sqsQueues = (urls) => ({
|
|
|
19
22
|
}),
|
|
20
23
|
body: createStandardQueueProducer({ queueUrl: urls.body }),
|
|
21
24
|
});
|
|
22
|
-
export const buildSharedDeps = (queues) =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
export const buildSharedDeps = (queues) => {
|
|
26
|
+
const imapTransport = nodeTransport();
|
|
27
|
+
return {
|
|
28
|
+
storage: createStorageService(),
|
|
29
|
+
secrets: createSecretsService(createCachedDataKeyProvider(createKmsDataKeyProvider(process.env.KMS_KEY_ID ?? FAKE_KMS_KEY_ID))),
|
|
30
|
+
connectionScope: passwordConnectionScope(imapTransport),
|
|
31
|
+
mailTransports: { imap: () => imapTransport, smtp: directSmtpSocket },
|
|
32
|
+
queues,
|
|
33
|
+
};
|
|
34
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/backend",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.149",
|
|
4
4
|
"description": "Remit Mail Inspector API backend",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"@remit/mail-oauth-service": "*",
|
|
102
102
|
"@remit/secrets-service": "*",
|
|
103
103
|
"@remit/mailbox-service": "*",
|
|
104
|
+
"@remit/smtp-service": "*",
|
|
104
105
|
"@remit/storage-service": "*",
|
|
105
106
|
"@remit/auth-service": "*",
|
|
106
107
|
"@remit/drizzle-service": "*",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { IAccountConfigRepository } from "@remit/data-ports";
|
|
2
|
-
/**
|
|
3
|
-
* Ensure an AccountConfig row exists for the given id. A first-time caller
|
|
4
|
-
* coming from Cognito will not yet have one; subsequent calls are idempotent.
|
|
5
|
-
*
|
|
6
|
-
* We attempt a read first to avoid relying on a race on `create()` — the
|
|
7
|
-
* deterministic id (derived from Cognito `sub`) makes both paths safe.
|
|
8
|
-
*/
|
|
9
|
-
export declare const ensureAccountConfig: (accountConfig: IAccountConfigRepository, accountConfigId: string) => Promise<void>;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ConflictError, NotFoundError } from "@remit/data-ports/errors";
|
|
2
|
-
/**
|
|
3
|
-
* Ensure an AccountConfig row exists for the given id. A first-time caller
|
|
4
|
-
* coming from Cognito will not yet have one; subsequent calls are idempotent.
|
|
5
|
-
*
|
|
6
|
-
* We attempt a read first to avoid relying on a race on `create()` — the
|
|
7
|
-
* deterministic id (derived from Cognito `sub`) makes both paths safe.
|
|
8
|
-
*/
|
|
9
|
-
export const ensureAccountConfig = async (accountConfig, accountConfigId) => {
|
|
10
|
-
const existing = await accountConfig.get(accountConfigId).catch((err) => {
|
|
11
|
-
if (err instanceof NotFoundError)
|
|
12
|
-
return undefined;
|
|
13
|
-
throw err;
|
|
14
|
-
});
|
|
15
|
-
if (existing)
|
|
16
|
-
return;
|
|
17
|
-
await accountConfig
|
|
18
|
-
.create({
|
|
19
|
-
accountConfigId,
|
|
20
|
-
userId: accountConfigId,
|
|
21
|
-
})
|
|
22
|
-
.catch((err) => {
|
|
23
|
-
if (err instanceof ConflictError)
|
|
24
|
-
return;
|
|
25
|
-
throw err;
|
|
26
|
-
});
|
|
27
|
-
};
|