@usequark/quark-create-app 1.25.15 → 1.26.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/package.json +2 -3
- package/src/index.js +10 -3
- package/templates/base-project/apps/web/src/app/api/auth/forgot-password/route.js +57 -0
- package/templates/base-project/apps/web/src/app/api/auth/register/route.js +20 -1
- package/templates/base-project/apps/web/src/app/api/auth/reset-password/route.js +104 -0
- package/templates/base-project/apps/web/src/app/api/auth/verify-email/route.js +58 -0
- package/templates/base-project/apps/web/src/app/auth/forgot-password/ForgotPasswordPageClient.js +159 -0
- package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +2 -51
- package/templates/base-project/apps/web/src/app/auth/reset-password/ResetPasswordPageClient.js +280 -0
- package/templates/base-project/apps/web/src/app/auth/reset-password/page.js +9 -0
- package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +22 -0
- package/templates/base-project/apps/web/src/lib/enqueue-email-job.js +67 -0
- package/templates/base-project/apps/web/src/lib/enqueue-reset-password-email.js +33 -0
- package/templates/base-project/apps/web/src/lib/enqueue-verification-email.js +25 -0
- package/templates/base-project/apps/web/src/lib/enqueue-welcome-email.js +13 -48
- package/templates/base-project/apps/web/src/lib/issue-verification-token.js +52 -0
- package/templates/base-project/apps/web/src/lib/proxy-auth.js +7 -0
- package/templates/base-project/apps/web/src/lib/send-reset-password-email.js +24 -0
- package/templates/base-project/apps/web/src/lib/send-verification-email.js +28 -0
- package/templates/base-project/packages/db/src/queries.js +13 -0
- package/templates/base-project/packages/db/src/schemas.js +16 -6
- package/templates/jobs/src/definitions.js +1 -0
- package/templates/worker/src/handlers/email-job-validation.js +13 -0
- package/templates/worker/src/handlers/email.js +39 -0
- package/templates/worker/src/handlers/index.js +2 -0
|
@@ -6,14 +6,24 @@ export const userCreateSchema = z.object({
|
|
|
6
6
|
image: z.string().url().optional(),
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Password rules, shared by every route that sets one.
|
|
11
|
+
*
|
|
12
|
+
* Registration and password reset must not drift: a reset that accepts a weaker
|
|
13
|
+
* password than signup would let a user downgrade their own credential, and the
|
|
14
|
+
* reverse would lock a legitimate password out of recovery. Composed into
|
|
15
|
+
* `userRegisterSchema` rather than duplicated, so there is one definition.
|
|
16
|
+
*/
|
|
17
|
+
export const passwordSchema = z
|
|
18
|
+
.string()
|
|
19
|
+
.min(8, "Password must be at least 8 characters")
|
|
20
|
+
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
|
|
21
|
+
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
|
|
22
|
+
.regex(/[0-9]/, "Password must contain at least one number");
|
|
23
|
+
|
|
9
24
|
export const userRegisterSchema = z.object({
|
|
10
25
|
email: z.string().email("Invalid email address"),
|
|
11
|
-
password:
|
|
12
|
-
.string()
|
|
13
|
-
.min(8, "Password must be at least 8 characters")
|
|
14
|
-
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
|
|
15
|
-
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
|
|
16
|
-
.regex(/[0-9]/, "Password must contain at least one number"),
|
|
26
|
+
password: passwordSchema,
|
|
17
27
|
name: z.string().min(2, "Name must be at least 2 characters").optional(),
|
|
18
28
|
});
|
|
19
29
|
|
|
@@ -23,6 +23,19 @@ export function requireResetPasswordEmailData(data) {
|
|
|
23
23
|
return { userId, resetUrl };
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function requireVerifyEmailData(data) {
|
|
27
|
+
const userId = data?.userId;
|
|
28
|
+
const verifyUrl = data?.verifyUrl;
|
|
29
|
+
|
|
30
|
+
if (!userId || !verifyUrl) {
|
|
31
|
+
throw new ValidationError(
|
|
32
|
+
"userId and verifyUrl are required for SEND_VERIFY_EMAIL job",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { userId, verifyUrl };
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
export function requireUserEmailRecord(userId, userRecord) {
|
|
27
40
|
if (!userRecord?.email) {
|
|
28
41
|
throw new AppError(
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { createEmailService } from "@usequark/quark-core/email";
|
|
7
7
|
import {
|
|
8
|
+
emailVerificationEmail,
|
|
8
9
|
passwordResetEmail,
|
|
9
10
|
welcomeEmail,
|
|
10
11
|
} from "@usequark/quark-core/email-templates";
|
|
@@ -13,6 +14,7 @@ import { JOB_NAMES } from "@usequark/quark-jobs";
|
|
|
13
14
|
import {
|
|
14
15
|
requireResetPasswordEmailData,
|
|
15
16
|
requireUserEmailRecord,
|
|
17
|
+
requireVerifyEmailData,
|
|
16
18
|
requireWelcomeEmailUserId,
|
|
17
19
|
} from "./email-job-validation.js";
|
|
18
20
|
|
|
@@ -94,5 +96,42 @@ export async function handleSendResetPasswordEmail(bullJob, logger) {
|
|
|
94
96
|
return { success: true, userId, email: userRecord.email };
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Job handler for SEND_VERIFY_EMAIL
|
|
101
|
+
* @param {import("bullmq").Job} bullJob
|
|
102
|
+
* @param {import("@usequark/quark-core").Logger} logger
|
|
103
|
+
*/
|
|
104
|
+
export async function handleSendVerificationEmail(bullJob, logger) {
|
|
105
|
+
const { userId, verifyUrl } = requireVerifyEmailData(bullJob.data);
|
|
106
|
+
|
|
107
|
+
logger.info(`Sending email verification for user ${userId}`, {
|
|
108
|
+
job: JOB_NAMES.SEND_VERIFY_EMAIL,
|
|
109
|
+
userId,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const userRecord = requireUserEmailRecord(
|
|
113
|
+
userId,
|
|
114
|
+
await prisma.user.findUnique({
|
|
115
|
+
where: { id: userId },
|
|
116
|
+
select: { email: true, name: true },
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const template = emailVerificationEmail({
|
|
121
|
+
name: userRecord.name,
|
|
122
|
+
appName: process.env.APP_NAME,
|
|
123
|
+
verifyUrl,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await emailService.sendEmail(
|
|
127
|
+
userRecord.email,
|
|
128
|
+
template.subject,
|
|
129
|
+
template.html,
|
|
130
|
+
template.text,
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return { success: true, userId, email: userRecord.email };
|
|
134
|
+
}
|
|
135
|
+
|
|
97
136
|
// Governance alerts are sent inline by the governance-check handler.
|
|
98
137
|
// No standalone handler needed in Quark.
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
import { JOB_NAMES } from "@usequark/quark-jobs";
|
|
8
8
|
import {
|
|
9
9
|
handleSendResetPasswordEmail,
|
|
10
|
+
handleSendVerificationEmail,
|
|
10
11
|
handleSendWelcomeEmail,
|
|
11
12
|
} from "./email.js";
|
|
12
13
|
import { handleCleanupOrphanedFiles } from "./files.js";
|
|
13
14
|
|
|
14
15
|
export const jobHandlers = {
|
|
15
16
|
[JOB_NAMES.SEND_WELCOME_EMAIL]: handleSendWelcomeEmail,
|
|
17
|
+
[JOB_NAMES.SEND_VERIFY_EMAIL]: handleSendVerificationEmail,
|
|
16
18
|
[JOB_NAMES.SEND_RESET_PASSWORD_EMAIL]: handleSendResetPasswordEmail,
|
|
17
19
|
[JOB_NAMES.CLEANUP_ORPHANED_FILES]: handleCleanupOrphanedFiles,
|
|
18
20
|
};
|