@zlooks.cn/password-auth-server 1.0.0
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/.hile-rsc/build-mtie8wg2-ca38f847/client-browser/chunks/chunk-Q3WJINFG.js +48386 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-browser/src-plugin-page-frame-07c4de56a6-3F24AYCH.css +170 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-browser/src-plugin-page-frame-07c4de56a6-OCDODDFY.js +440 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-browser/src-plugin-password-form-3bd63d69e5-CCNUYY62.js +706 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-browser/src-plugin-password-form-3bd63d69e5-TRYMJX4F.css +195 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-ssr/chunks/chunk-36DACA7C.js +155814 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-ssr/src-plugin-page-frame-07c4de56a6-SYE4RRNX.js +441 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/client-ssr/src-plugin-password-form-3bd63d69e5-YVCPJXLL.js +708 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/plugin.json +90 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/server-rsc/index.js +15298 -0
- package/.hile-rsc/build-mtie8wg2-ca38f847/styles/4b3193c4f2b317ffed425ccf401342a4bdd6ccfd5f9c42def3315e776f166975.css +385 -0
- package/dist/command/index.d.ts +3 -0
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +24 -0
- package/dist/command/index.js.map +1 -0
- package/dist/config/schema.d.ts +72 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +48 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/email/nodemailer-verification-email-sender.d.ts +11 -0
- package/dist/email/nodemailer-verification-email-sender.d.ts.map +1 -0
- package/dist/email/nodemailer-verification-email-sender.js +54 -0
- package/dist/email/nodemailer-verification-email-sender.js.map +1 -0
- package/dist/email/verification-code-rate-limiter.d.ts +14 -0
- package/dist/email/verification-code-rate-limiter.d.ts.map +1 -0
- package/dist/email/verification-code-rate-limiter.js +27 -0
- package/dist/email/verification-code-rate-limiter.js.map +1 -0
- package/dist/email/verification-email-sender.d.ts +10 -0
- package/dist/email/verification-email-sender.d.ts.map +1 -0
- package/dist/email/verification-email-sender.js +2 -0
- package/dist/email/verification-email-sender.js.map +1 -0
- package/dist/messages/auth-extension/begin.msg.d.ts +3 -0
- package/dist/messages/auth-extension/begin.msg.d.ts.map +1 -0
- package/dist/messages/auth-extension/begin.msg.js +8 -0
- package/dist/messages/auth-extension/begin.msg.js.map +1 -0
- package/dist/messages/auth-extension/complete.msg.d.ts +3 -0
- package/dist/messages/auth-extension/complete.msg.d.ts.map +1 -0
- package/dist/messages/auth-extension/complete.msg.js +8 -0
- package/dist/messages/auth-extension/complete.msg.js.map +1 -0
- package/dist/migrations/index.d.ts +3 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +2 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/password/attempt-coordinator.d.ts +12 -0
- package/dist/password/attempt-coordinator.d.ts.map +1 -0
- package/dist/password/attempt-coordinator.js +25 -0
- package/dist/password/attempt-coordinator.js.map +1 -0
- package/dist/password/credential-lifecycle-outbox.d.ts +42 -0
- package/dist/password/credential-lifecycle-outbox.d.ts.map +1 -0
- package/dist/password/credential-lifecycle-outbox.js +119 -0
- package/dist/password/credential-lifecycle-outbox.js.map +1 -0
- package/dist/password/credential-repository.d.ts +41 -0
- package/dist/password/credential-repository.d.ts.map +1 -0
- package/dist/password/credential-repository.js +64 -0
- package/dist/password/credential-repository.js.map +1 -0
- package/dist/password/password-authenticator.d.ts +59 -0
- package/dist/password/password-authenticator.d.ts.map +1 -0
- package/dist/password/password-authenticator.js +184 -0
- package/dist/password/password-authenticator.js.map +1 -0
- package/dist/password/typeorm-credential-repository.d.ts +15 -0
- package/dist/password/typeorm-credential-repository.d.ts.map +1 -0
- package/dist/password/typeorm-credential-repository.js +125 -0
- package/dist/password/typeorm-credential-repository.js.map +1 -0
- package/dist/persistence/entities.d.ts +29 -0
- package/dist/persistence/entities.d.ts.map +1 -0
- package/dist/persistence/entities.js +49 -0
- package/dist/persistence/entities.js.map +1 -0
- package/dist/runtime/auth-announcement-publisher.d.ts +18 -0
- package/dist/runtime/auth-announcement-publisher.d.ts.map +1 -0
- package/dist/runtime/auth-announcement-publisher.js +106 -0
- package/dist/runtime/auth-announcement-publisher.js.map +1 -0
- package/dist/runtime/password-auth-runtime.d.ts +33 -0
- package/dist/runtime/password-auth-runtime.d.ts.map +1 -0
- package/dist/runtime/password-auth-runtime.js +218 -0
- package/dist/runtime/password-auth-runtime.js.map +1 -0
- package/dist/runtime/password-state-store.d.ts +49 -0
- package/dist/runtime/password-state-store.d.ts.map +1 -0
- package/dist/runtime/password-state-store.js +130 -0
- package/dist/runtime/password-state-store.js.map +1 -0
- package/dist/service-identity.d.ts +2 -0
- package/dist/service-identity.d.ts.map +1 -0
- package/dist/service-identity.js +2 -0
- package/dist/service-identity.js.map +1 -0
- package/dist/services/application.service.d.ts +8 -0
- package/dist/services/application.service.d.ts.map +1 -0
- package/dist/services/application.service.js +22 -0
- package/dist/services/application.service.js.map +1 -0
- package/dist/services/config.service.d.ts +63 -0
- package/dist/services/config.service.d.ts.map +1 -0
- package/dist/services/config.service.js +20 -0
- package/dist/services/config.service.js.map +1 -0
- package/dist/services/database.service.d.ts +3 -0
- package/dist/services/database.service.d.ts.map +1 -0
- package/dist/services/database.service.js +23 -0
- package/dist/services/database.service.js.map +1 -0
- package/dist/services/email.service.d.ts +4 -0
- package/dist/services/email.service.d.ts.map +1 -0
- package/dist/services/email.service.js +37 -0
- package/dist/services/email.service.js.map +1 -0
- package/dist/services/logger.service.d.ts +4 -0
- package/dist/services/logger.service.d.ts.map +1 -0
- package/dist/services/logger.service.js +8 -0
- package/dist/services/logger.service.js.map +1 -0
- package/dist/services/password-auth.boot.d.ts +3 -0
- package/dist/services/password-auth.boot.d.ts.map +1 -0
- package/dist/services/password-auth.boot.js +4 -0
- package/dist/services/password-auth.boot.js.map +1 -0
- package/dist/services/password-runtime.service.d.ts +4 -0
- package/dist/services/password-runtime.service.d.ts.map +1 -0
- package/dist/services/password-runtime.service.js +135 -0
- package/dist/services/password-runtime.service.js.map +1 -0
- package/dist/services/redis.service.d.ts +4 -0
- package/dist/services/redis.service.d.ts.map +1 -0
- package/dist/services/redis.service.js +18 -0
- package/dist/services/redis.service.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { createRegistryDatabaseDataSource, registerDatabaseCommands, } from '@zlooks.cn/database-command';
|
|
4
|
+
import { runServiceCommand } from '@zlooks.cn/service-command';
|
|
5
|
+
import { passwordAuthMigrations } from '../migrations/index.js';
|
|
6
|
+
import { passwordAuthEntities } from '../persistence/entities.js';
|
|
7
|
+
await runServiceCommand({
|
|
8
|
+
packageRoot: fileURLToPath(new URL('../../', import.meta.url)),
|
|
9
|
+
requiresReactServer: true,
|
|
10
|
+
configure(program) {
|
|
11
|
+
registerDatabaseCommands(program, {
|
|
12
|
+
createDataSource: (envFile) => createRegistryDatabaseDataSource(envFile, {
|
|
13
|
+
commandNamespace: 'cn.zlooks.password.auth.db.command',
|
|
14
|
+
entities: [...passwordAuthEntities],
|
|
15
|
+
}),
|
|
16
|
+
migrations: passwordAuthMigrations,
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
}).catch(reportStartupFailure);
|
|
20
|
+
function reportStartupFailure(error) {
|
|
21
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE,MAAM,iBAAiB,CAAC;IACtB,WAAW,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9D,mBAAmB,EAAE,IAAI;IACzB,SAAS,CAAC,OAAO;QACf,wBAAwB,CAAC,OAAO,EAAE;YAChC,gBAAgB,EAAE,CAAC,OAAO,EAAE,EAAE,CAC5B,gCAAgC,CAAC,OAAO,EAAE;gBACxC,gBAAgB,EAAE,oCAAoC;gBACtD,QAAQ,EAAE,CAAC,GAAG,oBAAoB,CAAC;aACpC,CAAC;YACJ,UAAU,EAAE,sBAAsB;SACnC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;AAE/B,SAAS,oBAAoB,CAAC,KAAc;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const schema: z.ZodObject<{
|
|
3
|
+
infrastructure: z.ZodObject<{
|
|
4
|
+
postgres: z.ZodObject<{
|
|
5
|
+
host: z.ZodString;
|
|
6
|
+
port: z.ZodNumber;
|
|
7
|
+
username: z.ZodString;
|
|
8
|
+
password: z.ZodString;
|
|
9
|
+
database: z.ZodString;
|
|
10
|
+
ssl: z.ZodBoolean;
|
|
11
|
+
poolSize: z.ZodNumber;
|
|
12
|
+
}, z.core.$strict>;
|
|
13
|
+
redis: z.ZodObject<{
|
|
14
|
+
host: z.ZodString;
|
|
15
|
+
port: z.ZodNumber;
|
|
16
|
+
username: z.ZodOptional<z.ZodString>;
|
|
17
|
+
password: z.ZodOptional<z.ZodString>;
|
|
18
|
+
database: z.ZodNumber;
|
|
19
|
+
keyPrefix: z.ZodString;
|
|
20
|
+
tls: z.ZodBoolean;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
email: z.ZodObject<{
|
|
23
|
+
smtp: z.ZodObject<{
|
|
24
|
+
host: z.ZodString;
|
|
25
|
+
port: z.ZodNumber;
|
|
26
|
+
secure: z.ZodBoolean;
|
|
27
|
+
username: z.ZodString;
|
|
28
|
+
password: z.ZodString;
|
|
29
|
+
}, z.core.$strict>;
|
|
30
|
+
from: z.ZodObject<{
|
|
31
|
+
name: z.ZodString;
|
|
32
|
+
address: z.ZodEmail;
|
|
33
|
+
}, z.core.$strict>;
|
|
34
|
+
}, z.core.$strict>;
|
|
35
|
+
}, z.core.$strict>;
|
|
36
|
+
discovery: z.ZodObject<{
|
|
37
|
+
instanceId: z.ZodLiteral<"password-auth">;
|
|
38
|
+
generation: z.ZodLiteral<2>;
|
|
39
|
+
leaseMs: z.ZodLiteral<10000>;
|
|
40
|
+
}, z.core.$strict>;
|
|
41
|
+
rsc: z.ZodObject<{
|
|
42
|
+
generation: z.ZodLiteral<2>;
|
|
43
|
+
priority: z.ZodLiteral<100>;
|
|
44
|
+
}, z.core.$strict>;
|
|
45
|
+
password: z.ZodObject<{
|
|
46
|
+
minLength: z.ZodLiteral<12>;
|
|
47
|
+
maxLength: z.ZodLiteral<128>;
|
|
48
|
+
memoryCostKiB: z.ZodLiteral<65536>;
|
|
49
|
+
timeCost: z.ZodLiteral<3>;
|
|
50
|
+
parallelism: z.ZodLiteral<1>;
|
|
51
|
+
maxFailedAttempts: z.ZodLiteral<5>;
|
|
52
|
+
lockoutMs: z.ZodLiteral<900000>;
|
|
53
|
+
}, z.core.$strict>;
|
|
54
|
+
verification: z.ZodObject<{
|
|
55
|
+
hmacKey: z.ZodString;
|
|
56
|
+
codeTtlMs: z.ZodLiteral<600000>;
|
|
57
|
+
resendIntervalMs: z.ZodLiteral<60000>;
|
|
58
|
+
maxSendsPerTransaction: z.ZodLiteral<3>;
|
|
59
|
+
maxSendsPerEmailPerHour: z.ZodLiteral<5>;
|
|
60
|
+
maxVerificationAttempts: z.ZodLiteral<5>;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
}, z.core.$strict>;
|
|
63
|
+
export interface PasswordAuthConfigSources {
|
|
64
|
+
readonly postgres: unknown;
|
|
65
|
+
readonly redis: unknown;
|
|
66
|
+
readonly email: unknown;
|
|
67
|
+
readonly passwordAuth: unknown;
|
|
68
|
+
}
|
|
69
|
+
export type PasswordAuthConfig = Readonly<z.infer<typeof schema>>;
|
|
70
|
+
export declare function parsePasswordAuthConfig(input: PasswordAuthConfigSources): PasswordAuthConfig;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAaV,CAAC;AAUH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AACD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC;AAClE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,yBAAyB,GAAG,kBAAkB,CAmB5F"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { emailConfigSchema, postgresConfigSchema, redisConfigSchema } from '@zlooks.cn/infrastructure';
|
|
2
|
+
import { deepFreeze, joinKeyPrefix } from '@zlooks.cn/utils';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const schema = z.strictObject({
|
|
5
|
+
infrastructure: z.strictObject({ postgres: postgresConfigSchema, redis: redisConfigSchema, email: emailConfigSchema }),
|
|
6
|
+
discovery: z.strictObject({ instanceId: z.literal('password-auth'), generation: z.literal(2), leaseMs: z.literal(10_000) }),
|
|
7
|
+
rsc: z.strictObject({ generation: z.literal(2), priority: z.literal(100) }),
|
|
8
|
+
password: z.strictObject({ minLength: z.literal(12), maxLength: z.literal(128), memoryCostKiB: z.literal(65_536), timeCost: z.literal(3), parallelism: z.literal(1), maxFailedAttempts: z.literal(5), lockoutMs: z.literal(900_000) }),
|
|
9
|
+
verification: z.strictObject({
|
|
10
|
+
hmacKey: z.string(),
|
|
11
|
+
codeTtlMs: z.literal(600_000),
|
|
12
|
+
resendIntervalMs: z.literal(60_000),
|
|
13
|
+
maxSendsPerTransaction: z.literal(3),
|
|
14
|
+
maxSendsPerEmailPerHour: z.literal(5),
|
|
15
|
+
maxVerificationAttempts: z.literal(5),
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
const passwordAuthSourceSchema = z.strictObject({
|
|
19
|
+
verificationCodeHmacKey: z.string().regex(/^[A-Za-z0-9+/]{43}=$/).refine((value) => {
|
|
20
|
+
try {
|
|
21
|
+
return Buffer.from(value, 'base64').byteLength === 32;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}, 'Verification code HMAC key must be a base64-encoded 32-byte key'),
|
|
27
|
+
});
|
|
28
|
+
export function parsePasswordAuthConfig(input) {
|
|
29
|
+
const postgres = postgresConfigSchema.parse(input.postgres);
|
|
30
|
+
const redis = redisConfigSchema.parse(input.redis);
|
|
31
|
+
const email = emailConfigSchema.parse(input.email);
|
|
32
|
+
const passwordAuth = passwordAuthSourceSchema.parse(input.passwordAuth);
|
|
33
|
+
return deepFreeze(schema.parse({
|
|
34
|
+
infrastructure: { postgres, redis: { ...redis, keyPrefix: joinKeyPrefix(redis.keyPrefix, 'password-auth') }, email },
|
|
35
|
+
discovery: { instanceId: 'password-auth', generation: 2, leaseMs: 10_000 },
|
|
36
|
+
rsc: { generation: 2, priority: 100 },
|
|
37
|
+
password: { minLength: 12, maxLength: 128, memoryCostKiB: 65_536, timeCost: 3, parallelism: 1, maxFailedAttempts: 5, lockoutMs: 900_000 },
|
|
38
|
+
verification: {
|
|
39
|
+
hmacKey: passwordAuth.verificationCodeHmacKey,
|
|
40
|
+
codeTtlMs: 600_000,
|
|
41
|
+
resendIntervalMs: 60_000,
|
|
42
|
+
maxSendsPerTransaction: 3,
|
|
43
|
+
maxSendsPerEmailPerHour: 5,
|
|
44
|
+
maxVerificationAttempts: 5,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Transporter } from 'nodemailer';
|
|
2
|
+
import type { EmailConfig } from '@zlooks.cn/infrastructure';
|
|
3
|
+
import type { VerificationEmailSender } from './verification-email-sender.js';
|
|
4
|
+
export declare class NodemailerVerificationEmailSender implements VerificationEmailSender {
|
|
5
|
+
#private;
|
|
6
|
+
private readonly config;
|
|
7
|
+
constructor(config: EmailConfig, transporter?: Transporter);
|
|
8
|
+
send(input: Parameters<VerificationEmailSender['send']>[0]): Promise<void>;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=nodemailer-verification-email-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodemailer-verification-email-sender.d.ts","sourceRoot":"","sources":["../../src/email/nodemailer-verification-email-sender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE9E,qBAAa,iCAAkC,YAAW,uBAAuB;;IAK5D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAA1C,YAAoC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW,EAWjF;IAEY,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtF;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAIlC;CACF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
export class NodemailerVerificationEmailSender {
|
|
3
|
+
config;
|
|
4
|
+
#transporter;
|
|
5
|
+
#inFlight = new Set();
|
|
6
|
+
#closing = false;
|
|
7
|
+
constructor(config, transporter) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.#transporter = transporter ?? nodemailer.createTransport({
|
|
10
|
+
host: config.smtp.host,
|
|
11
|
+
port: config.smtp.port,
|
|
12
|
+
secure: config.smtp.secure,
|
|
13
|
+
auth: { user: config.smtp.username, pass: config.smtp.password },
|
|
14
|
+
pool: true,
|
|
15
|
+
connectionTimeout: 10_000,
|
|
16
|
+
greetingTimeout: 10_000,
|
|
17
|
+
socketTimeout: 20_000,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async send(input) {
|
|
21
|
+
if (this.#closing)
|
|
22
|
+
throw new Error('Email delivery is shutting down');
|
|
23
|
+
const purpose = input.intent === 'registration'
|
|
24
|
+
? '注册账号'
|
|
25
|
+
: input.intent === 'recovery'
|
|
26
|
+
? '找回密码'
|
|
27
|
+
: '登录账号';
|
|
28
|
+
const operation = Promise.resolve().then(() => this.#transporter.sendMail({
|
|
29
|
+
from: { name: this.config.from.name, address: this.config.from.address },
|
|
30
|
+
to: input.email,
|
|
31
|
+
subject: `ZLOOKS ${purpose}验证码`,
|
|
32
|
+
text: `你的验证码是 ${input.code},${input.expiresInMinutes} 分钟内有效。请勿将验证码告诉他人。`,
|
|
33
|
+
html: `<p>你的验证码是:</p><p style="font-size:28px;font-weight:700;letter-spacing:6px">${input.code}</p><p>${input.expiresInMinutes} 分钟内有效。请勿将验证码告诉他人。</p>`,
|
|
34
|
+
}));
|
|
35
|
+
this.#inFlight.add(operation);
|
|
36
|
+
let result;
|
|
37
|
+
try {
|
|
38
|
+
result = await operation;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
this.#inFlight.delete(operation);
|
|
42
|
+
}
|
|
43
|
+
if (!Array.isArray(result.accepted)
|
|
44
|
+
|| !result.accepted.some((address) => String(address).toLocaleLowerCase('en-US') === input.email)) {
|
|
45
|
+
throw new Error('SMTP server did not accept the verification email recipient');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
this.#closing = true;
|
|
50
|
+
await Promise.allSettled(this.#inFlight);
|
|
51
|
+
this.#transporter.close();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=nodemailer-verification-email-sender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodemailer-verification-email-sender.js","sourceRoot":"","sources":["../../src/email/nodemailer-verification-email-sender.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,YAAY,CAAC;AAIpC,MAAM,OAAO,iCAAiC;IAKR,MAAM;IAJjC,YAAY,CAAc;IAC1B,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,QAAQ,GAAG,KAAK,CAAC;IAEjB,YAAoC,MAAmB,EAAE,WAAyB;sBAA9C,MAAM;QACxC,IAAI,CAAC,YAAY,GAAG,WAAW,IAAI,UAAU,CAAC,eAAe,CAAC;YAC5D,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;YACtB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI;YACtB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE;YAChE,IAAI,EAAE,IAAI;YACV,iBAAiB,EAAE,MAAM;YACzB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,MAAM;SACtB,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,KAAqD;QACrE,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,cAAc;YAC7C,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU;gBAC3B,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,MAAM,CAAC;QACb,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;YACxE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE;YACxE,EAAE,EAAE,KAAK,CAAC,KAAK;YACf,OAAO,EAAE,UAAU,OAAO,KAAK;YAC/B,IAAI,EAAE,UAAU,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,gBAAgB,oBAAoB;YACxE,IAAI,EAAE,8EAA8E,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC,gBAAgB,wBAAwB;SACvJ,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,MAAiC,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,SAAS,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;eAC9B,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7G,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuthenticationIntent } from '@zlooks.cn/auth-extension-contract';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
export interface VerificationCodeRateLimiter {
|
|
4
|
+
consume(email: string, intent: Extract<AuthenticationIntent, 'login' | 'registration' | 'recovery'>): Promise<boolean>;
|
|
5
|
+
}
|
|
6
|
+
export declare class RedisVerificationCodeRateLimiter implements VerificationCodeRateLimiter {
|
|
7
|
+
#private;
|
|
8
|
+
private readonly limit;
|
|
9
|
+
private readonly hmacKey;
|
|
10
|
+
constructor(redis: Redis, limit: number, hmacKey: string);
|
|
11
|
+
consume(email: string, intent: Extract<AuthenticationIntent, 'login' | 'registration' | 'recovery'>): Promise<boolean>;
|
|
12
|
+
}
|
|
13
|
+
export declare function verificationRateLimitIdentity(email: string, hmacKey: string): string;
|
|
14
|
+
//# sourceMappingURL=verification-code-rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-code-rate-limiter.d.ts","sourceRoot":"","sources":["../../src/email/verification-code-rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,MAAM,WAAW,2BAA2B;IAC1C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxH;AAED,qBAAa,gCAAiC,YAAW,2BAA2B;;IAKhF,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,YACE,KAAK,EAAE,KAAK,EACK,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAGjC;IAEY,OAAO,CAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,GAC3E,OAAO,CAAC,OAAO,CAAC,CAOlB;CACF;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKpF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineLimit, RedisRateLimiter } from '@hile/redis-rate-limit';
|
|
2
|
+
import { createHmac } from 'node:crypto';
|
|
3
|
+
export class RedisVerificationCodeRateLimiter {
|
|
4
|
+
limit;
|
|
5
|
+
hmacKey;
|
|
6
|
+
#limiter;
|
|
7
|
+
constructor(redis, limit, hmacKey) {
|
|
8
|
+
this.limit = limit;
|
|
9
|
+
this.hmacKey = hmacKey;
|
|
10
|
+
this.#limiter = new RedisRateLimiter(redis, { prefix: 'verification-rate-limit:' });
|
|
11
|
+
}
|
|
12
|
+
async consume(email, intent) {
|
|
13
|
+
const result = await this.#limiter.consume(defineLimit('email-code:{intent:string}:{email:string}', {
|
|
14
|
+
algorithm: 'sliding-window',
|
|
15
|
+
limit: this.limit,
|
|
16
|
+
window: 3_600_000,
|
|
17
|
+
}), { intent, email: verificationRateLimitIdentity(email, this.hmacKey) });
|
|
18
|
+
return result.allowed;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function verificationRateLimitIdentity(email, hmacKey) {
|
|
22
|
+
return createHmac('sha256', Buffer.from(hmacKey, 'base64'))
|
|
23
|
+
.update('email-verification-rate-limit\0')
|
|
24
|
+
.update(email)
|
|
25
|
+
.digest('hex');
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=verification-code-rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-code-rate-limiter.js","sourceRoot":"","sources":["../../src/email/verification-code-rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAGvE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMzC,MAAM,OAAO,gCAAgC;IAKxB,KAAK;IACL,OAAO;IALjB,QAAQ,CAAmB;IAEpC,YACE,KAAY,EACK,KAAa,EACb,OAAe;qBADf,KAAK;uBACL,OAAO;QAExB,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC,CAAC;IACtF,CAAC;IAEM,KAAK,CAAC,OAAO,CAClB,KAAa,EACb,MAA4E;QAE5E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,2CAA2C,EAAE;YAClG,SAAS,EAAE,gBAAgB;YAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,SAAS;SAClB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,6BAA6B,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3E,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;CACF;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAa,EAAE,OAAe;IAC1E,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;SACxD,MAAM,CAAC,iCAAiC,CAAC;SACzC,MAAM,CAAC,KAAK,CAAC;SACb,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuthenticationIntent } from '@zlooks.cn/auth-extension-contract';
|
|
2
|
+
export interface VerificationEmailSender {
|
|
3
|
+
send(input: Readonly<{
|
|
4
|
+
email: string;
|
|
5
|
+
code: string;
|
|
6
|
+
intent: Extract<AuthenticationIntent, 'login' | 'registration' | 'recovery'>;
|
|
7
|
+
expiresInMinutes: number;
|
|
8
|
+
}>): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=verification-email-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-email-sender.d.ts","sourceRoot":"","sources":["../../src/email/verification-email-sender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,CAAC;QAC7E,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-email-sender.js","sourceRoot":"","sources":["../../src/email/verification-email-sender.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"begin.msg.d.ts","sourceRoot":"","sources":["../../../src/messages/auth-extension/begin.msg.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { loadService } from '@hile/core';
|
|
2
|
+
import { defineMicroMessage } from '@hile/micro';
|
|
3
|
+
import passwordRuntimeService from '../../services/password-runtime.service.js';
|
|
4
|
+
export default defineMicroMessage(async ({ data }) => {
|
|
5
|
+
const runtime = await loadService(passwordRuntimeService);
|
|
6
|
+
return runtime.begin(data);
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=begin.msg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"begin.msg.js","sourceRoot":"","sources":["../../../src/messages/auth-extension/begin.msg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAEhF,eAAe,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACnD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAC1D,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete.msg.d.ts","sourceRoot":"","sources":["../../../src/messages/auth-extension/complete.msg.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { loadService } from '@hile/core';
|
|
2
|
+
import { defineMicroMessage } from '@hile/micro';
|
|
3
|
+
import passwordRuntimeService from '../../services/password-runtime.service.js';
|
|
4
|
+
export default defineMicroMessage(async ({ data }) => {
|
|
5
|
+
const runtime = await loadService(passwordRuntimeService);
|
|
6
|
+
return runtime.complete(data);
|
|
7
|
+
});
|
|
8
|
+
//# sourceMappingURL=complete.msg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete.msg.js","sourceRoot":"","sources":["../../../src/messages/auth-extension/complete.msg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,sBAAsB,MAAM,4CAA4C,CAAC;AAEhF,eAAe,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACnD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAC1D,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,eAAO,MAAM,sBAAsB,EAAE,SAAS,iBAAiB,EAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,sBAAsB,GAAiC,EAAE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
export interface PasswordAttemptCoordinator {
|
|
3
|
+
run<T>(identifier: string, operation: () => Promise<T>): Promise<T>;
|
|
4
|
+
}
|
|
5
|
+
export declare class RedisPasswordAttemptCoordinator implements PasswordAttemptCoordinator {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly hmacKey;
|
|
8
|
+
constructor(redis: Redis, hmacKey: string);
|
|
9
|
+
run<T>(identifier: string, operation: () => Promise<T>): Promise<T>;
|
|
10
|
+
}
|
|
11
|
+
export declare function passwordAttemptIdentity(identifier: string, hmacKey: string): string;
|
|
12
|
+
//# sourceMappingURL=attempt-coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-coordinator.d.ts","sourceRoot":"","sources":["../../src/password/attempt-coordinator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,0BAA0B;IACzC,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;AAED,qBAAa,+BAAgC,YAAW,0BAA0B;;IAG/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAzD,YAAmB,KAAK,EAAE,KAAK,EAAmB,OAAO,EAAE,MAAM,EAEhE;IAEY,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO/E;CACF;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKnF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
2
|
+
import { RedisLock } from '@hile/redis-lock';
|
|
3
|
+
export class RedisPasswordAttemptCoordinator {
|
|
4
|
+
hmacKey;
|
|
5
|
+
#locks;
|
|
6
|
+
constructor(redis, hmacKey) {
|
|
7
|
+
this.hmacKey = hmacKey;
|
|
8
|
+
this.#locks = new RedisLock(redis, { prefix: 'locks:', defaultTtl: 30_000 });
|
|
9
|
+
}
|
|
10
|
+
async run(identifier, operation) {
|
|
11
|
+
const key = passwordAttemptIdentity(identifier, this.hmacKey);
|
|
12
|
+
return this.#locks.withLock(`password-attempt:${key}`, {
|
|
13
|
+
wait: 5_000,
|
|
14
|
+
ttl: 30_000,
|
|
15
|
+
renew: true,
|
|
16
|
+
}, async () => operation());
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function passwordAttemptIdentity(identifier, hmacKey) {
|
|
20
|
+
return createHmac('sha256', Buffer.from(hmacKey, 'base64'))
|
|
21
|
+
.update('password-attempt-lock\0')
|
|
22
|
+
.update(identifier)
|
|
23
|
+
.digest('hex');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=attempt-coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-coordinator.js","sourceRoot":"","sources":["../../src/password/attempt-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAO7C,MAAM,OAAO,+BAA+B;IAGQ,OAAO;IAFhD,MAAM,CAAY;IAE3B,YAAmB,KAAY,EAAmB,OAAe;uBAAf,OAAO;QACvD,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/E,CAAC;IAEM,KAAK,CAAC,GAAG,CAAI,UAAkB,EAAE,SAA2B;QACjE,MAAM,GAAG,GAAG,uBAAuB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,GAAG,EAAE,EAAE;YACrD,IAAI,EAAE,KAAK;YACX,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,IAAI;SACZ,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,UAAU,uBAAuB,CAAC,UAAkB,EAAE,OAAe;IACzE,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;SACxD,MAAM,CAAC,yBAAyB,CAAC;SACjC,MAAM,CAAC,UAAU,CAAC;SAClB,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type ExecutionContext } from '@hile/context';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
import { type DataSource } from 'typeorm';
|
|
4
|
+
import { type PasswordCredentialLifecycleOutboxRow } from '../persistence/entities.js';
|
|
5
|
+
export type PendingCredentialLifecycleEvent = Readonly<Pick<PasswordCredentialLifecycleOutboxRow, 'transactionId' | 'kind' | 'subject' | 'identifier' | 'occurredAt' | 'attempts'>>;
|
|
6
|
+
export interface PasswordCredentialLifecycleOutboxStore {
|
|
7
|
+
readDue(now: Date, limit: number): Promise<readonly PendingCredentialLifecycleEvent[]>;
|
|
8
|
+
acknowledge(transactionId: string): Promise<void>;
|
|
9
|
+
recordFailure(transactionId: string, attempts: number, nextAttemptAt: Date): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export interface CredentialLifecycleMicroCaller {
|
|
12
|
+
call(namespace: string, operation: string, data: unknown, options: Readonly<{
|
|
13
|
+
context: ExecutionContext;
|
|
14
|
+
timeout: number;
|
|
15
|
+
retries: number;
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}>): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
export declare class TypeOrmPasswordCredentialLifecycleOutboxStore implements PasswordCredentialLifecycleOutboxStore {
|
|
20
|
+
private readonly dataSource;
|
|
21
|
+
constructor(dataSource: DataSource);
|
|
22
|
+
readDue(now: Date, limit: number): Promise<readonly PendingCredentialLifecycleEvent[]>;
|
|
23
|
+
acknowledge(transactionId: string): Promise<void>;
|
|
24
|
+
recordFailure(transactionId: string, attempts: number, nextAttemptAt: Date): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare class PasswordCredentialLifecyclePublisher {
|
|
27
|
+
#private;
|
|
28
|
+
private readonly options;
|
|
29
|
+
constructor(options: {
|
|
30
|
+
readonly application: CredentialLifecycleMicroCaller;
|
|
31
|
+
readonly redis: Redis;
|
|
32
|
+
readonly store: PasswordCredentialLifecycleOutboxStore;
|
|
33
|
+
readonly clock?: () => Date;
|
|
34
|
+
readonly onError?: (error: unknown, event?: PendingCredentialLifecycleEvent) => void;
|
|
35
|
+
});
|
|
36
|
+
start(): void;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
flush(): Promise<void>;
|
|
39
|
+
private runFlush;
|
|
40
|
+
private publish;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=credential-lifecycle-outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-lifecycle-outbox.d.ts","sourceRoot":"","sources":["../../src/password/credential-lifecycle-outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAQ9E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAEL,KAAK,oCAAoC,EAC1C,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,+BAA+B,GAAG,QAAQ,CAAC,IAAI,CACzD,oCAAoC,EACpC,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,CAChF,CAAC,CAAC;AAEH,MAAM,WAAW,sCAAsC;IACrD,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,+BAA+B,EAAE,CAAC,CAAC;IACvF,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,CACF,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,QAAQ,CAAC;QAChB,OAAO,EAAE,gBAAgB,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,WAAW,CAAC;KACrB,CAAC,GACD,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED,qBAAa,6CACb,YAAW,sCAAsC;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAA9C,YAAoC,UAAU,EAAE,UAAU,EAAI;IAEjD,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,+BAA+B,EAAE,CAAC,CAclG;IAEY,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7D;IAEY,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtG;CACF;AAED,qBAAa,oCAAoC;;IAO5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAA3C,YAAoC,OAAO,EAAE;QAC3C,QAAQ,CAAC,WAAW,EAAE,8BAA8B,CAAC;QACrD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,sCAAsC,CAAC;QACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;QAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,+BAA+B,KAAK,IAAI,CAAC;KACtF,EAEA;IAEM,KAAK,IAAI,IAAI,CAanB;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAKlC;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CASlC;YAEa,QAAQ;YAeR,OAAO;CAmCtB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createExecutionContext } from '@hile/context';
|
|
2
|
+
import { Scheduler } from '@hile/schedule';
|
|
3
|
+
import { parseUserCredentialLifecycleResponse, USER_CREDENTIAL_LIFECYCLE_OPERATION, USER_SERVICE_NAMESPACE, } from '@zlooks.cn/user-shared';
|
|
4
|
+
import { LessThanOrEqual } from 'typeorm';
|
|
5
|
+
import { PasswordCredentialLifecycleOutboxEntity, } from '../persistence/entities.js';
|
|
6
|
+
export class TypeOrmPasswordCredentialLifecycleOutboxStore {
|
|
7
|
+
dataSource;
|
|
8
|
+
constructor(dataSource) {
|
|
9
|
+
this.dataSource = dataSource;
|
|
10
|
+
}
|
|
11
|
+
async readDue(now, limit) {
|
|
12
|
+
return this.dataSource.getRepository(PasswordCredentialLifecycleOutboxEntity).find({
|
|
13
|
+
where: { nextAttemptAt: LessThanOrEqual(now) },
|
|
14
|
+
order: { createdAt: 'ASC' },
|
|
15
|
+
take: limit,
|
|
16
|
+
select: {
|
|
17
|
+
transactionId: true,
|
|
18
|
+
kind: true,
|
|
19
|
+
subject: true,
|
|
20
|
+
identifier: true,
|
|
21
|
+
occurredAt: true,
|
|
22
|
+
attempts: true,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async acknowledge(transactionId) {
|
|
27
|
+
await this.dataSource.getRepository(PasswordCredentialLifecycleOutboxEntity).delete({ transactionId });
|
|
28
|
+
}
|
|
29
|
+
async recordFailure(transactionId, attempts, nextAttemptAt) {
|
|
30
|
+
await this.dataSource.getRepository(PasswordCredentialLifecycleOutboxEntity).update({ transactionId }, { attempts, nextAttemptAt });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class PasswordCredentialLifecyclePublisher {
|
|
34
|
+
options;
|
|
35
|
+
#scheduler = new Scheduler();
|
|
36
|
+
#clock;
|
|
37
|
+
#inFlight = new Set();
|
|
38
|
+
#controllers = new Set();
|
|
39
|
+
#closing = false;
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
this.#clock = options.clock ?? (() => new Date());
|
|
43
|
+
}
|
|
44
|
+
start() {
|
|
45
|
+
this.#scheduler.add('credential-lifecycle-outbox', '*/2 * * * * *', async () => {
|
|
46
|
+
await this.flush();
|
|
47
|
+
}, {
|
|
48
|
+
distributed: {
|
|
49
|
+
redis: this.options.redis,
|
|
50
|
+
ttl: 30_000,
|
|
51
|
+
namespace: 'zlooks.password-auth',
|
|
52
|
+
policy: 'skip-if-locked',
|
|
53
|
+
renew: true,
|
|
54
|
+
},
|
|
55
|
+
onError: (error) => this.options.onError?.(error),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async close() {
|
|
59
|
+
this.#closing = true;
|
|
60
|
+
this.#scheduler.stop();
|
|
61
|
+
for (const controller of this.#controllers)
|
|
62
|
+
controller.abort();
|
|
63
|
+
await Promise.allSettled(this.#inFlight);
|
|
64
|
+
}
|
|
65
|
+
async flush() {
|
|
66
|
+
if (this.#closing)
|
|
67
|
+
return;
|
|
68
|
+
const operation = this.runFlush();
|
|
69
|
+
this.#inFlight.add(operation);
|
|
70
|
+
try {
|
|
71
|
+
await operation;
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
this.#inFlight.delete(operation);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async runFlush() {
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
this.#controllers.add(controller);
|
|
80
|
+
try {
|
|
81
|
+
const events = await this.options.store.readDue(this.#clock(), 20);
|
|
82
|
+
for (let offset = 0; offset < events.length && !controller.signal.aborted; offset += 5) {
|
|
83
|
+
await Promise.all(events.slice(offset, offset + 5).map((event) => this.publish(event, controller.signal)));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
this.#controllers.delete(controller);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async publish(event, signal) {
|
|
91
|
+
try {
|
|
92
|
+
const payload = {
|
|
93
|
+
protocol: 'zlooks.user.credential-lifecycle',
|
|
94
|
+
kind: event.kind,
|
|
95
|
+
authenticationTransactionId: event.transactionId,
|
|
96
|
+
extensionId: 'password',
|
|
97
|
+
methodId: 'password',
|
|
98
|
+
subject: event.subject,
|
|
99
|
+
email: event.identifier,
|
|
100
|
+
occurredAt: event.occurredAt.toISOString(),
|
|
101
|
+
};
|
|
102
|
+
parseUserCredentialLifecycleResponse(await this.options.application.call(USER_SERVICE_NAMESPACE, USER_CREDENTIAL_LIFECYCLE_OPERATION, payload, {
|
|
103
|
+
context: createExecutionContext({}),
|
|
104
|
+
timeout: 5_000,
|
|
105
|
+
retries: 0,
|
|
106
|
+
signal,
|
|
107
|
+
}));
|
|
108
|
+
await this.options.store.acknowledge(event.transactionId);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
const attempts = event.attempts + 1;
|
|
112
|
+
const delayMs = Math.min(60_000, 1_000 * (2 ** Math.min(attempts, 6)));
|
|
113
|
+
await this.options.store.recordFailure(event.transactionId, attempts, new Date(this.#clock().getTime() + delayMs));
|
|
114
|
+
if (!signal.aborted)
|
|
115
|
+
this.options.onError?.(error, event);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=credential-lifecycle-outbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-lifecycle-outbox.js","sourceRoot":"","sources":["../../src/password/credential-lifecycle-outbox.ts"],"names":[],"mappings":"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"}
|