@usequark/quark-create-app 1.25.16 → 1.27.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.
Files changed (40) hide show
  1. package/README.md +6 -8
  2. package/package.json +1 -1
  3. package/src/index.js +75 -30
  4. package/templates/base-project/apps/web/.env.example +10 -0
  5. package/templates/base-project/apps/web/src/app/api/auth/apple/route.js +24 -0
  6. package/templates/base-project/apps/web/src/app/api/auth/forgot-password/route.js +57 -0
  7. package/templates/base-project/apps/web/src/app/api/auth/google/route.js +5 -1
  8. package/templates/base-project/apps/web/src/app/api/auth/refresh/route.js +57 -8
  9. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +20 -1
  10. package/templates/base-project/apps/web/src/app/api/auth/reset-password/route.js +105 -0
  11. package/templates/base-project/apps/web/src/app/api/auth/verify-email/route.js +58 -0
  12. package/templates/base-project/apps/web/src/app/auth/forgot-password/ForgotPasswordPageClient.js +159 -0
  13. package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +2 -51
  14. package/templates/base-project/apps/web/src/app/auth/reset-password/ResetPasswordPageClient.js +280 -0
  15. package/templates/base-project/apps/web/src/app/auth/reset-password/page.js +9 -0
  16. package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +22 -0
  17. package/templates/base-project/apps/web/src/lib/auth-email-verification.js +5 -0
  18. package/templates/base-project/apps/web/src/lib/auth.js +31 -10
  19. package/templates/base-project/apps/web/src/lib/enqueue-email-job.js +67 -0
  20. package/templates/base-project/apps/web/src/lib/enqueue-reset-password-email.js +33 -0
  21. package/templates/base-project/apps/web/src/lib/enqueue-verification-email.js +25 -0
  22. package/templates/base-project/apps/web/src/lib/enqueue-welcome-email.js +13 -48
  23. package/templates/base-project/apps/web/src/lib/issue-verification-token.js +52 -0
  24. package/templates/base-project/apps/web/src/lib/proxy-auth.js +7 -0
  25. package/templates/base-project/apps/web/src/lib/send-reset-password-email.js +24 -0
  26. package/templates/base-project/apps/web/src/lib/send-verification-email.js +28 -0
  27. package/templates/base-project/apps/web/src/proxy.js +48 -7
  28. package/templates/base-project/packages/db/prisma/schema.prisma +7 -2
  29. package/templates/base-project/packages/db/src/queries.js +13 -0
  30. package/templates/base-project/packages/db/src/schemas.js +16 -6
  31. package/templates/config/src/environment.js +5 -0
  32. package/templates/config/src/load-config.js +8 -0
  33. package/templates/config/src/validate-env.js +5 -0
  34. package/templates/jobs/src/definitions.js +1 -0
  35. package/templates/worker/package.json +1 -0
  36. package/templates/worker/scripts/sync-readme.js +41 -0
  37. package/templates/worker/src/handlers/email-job-validation.js +13 -0
  38. package/templates/worker/src/handlers/email.js +39 -0
  39. package/templates/worker/src/handlers/index.js +2 -0
  40. package/templates/worker/src/readme-table.js +68 -0
@@ -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
  };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Generates the README job-handler table.
3
+ *
4
+ * The row set is driven by JOB_NAMES (the jobs package is the source of truth
5
+ * for which jobs exist), never by the registry alone: generating from
6
+ * `jobHandlers` would produce a table that always matches what is registered
7
+ * and so could never expose a job name with no handler.
8
+ */
9
+
10
+ import { AppError } from "@usequark/quark-core/errors";
11
+ import { JOB_NAMES } from "@usequark/quark-jobs";
12
+
13
+ /** Module under `src/handlers/` that defines each job's handler. */
14
+ const HANDLER_MODULES = {
15
+ [JOB_NAMES.SEND_WELCOME_EMAIL]: "email.js",
16
+ [JOB_NAMES.SEND_VERIFY_EMAIL]: "email.js",
17
+ [JOB_NAMES.SEND_RESET_PASSWORD_EMAIL]: "email.js",
18
+ [JOB_NAMES.CLEANUP_ORPHANED_FILES]: "files.js",
19
+ };
20
+
21
+ export const TABLE_START = "<!-- handler-table:start -->";
22
+ export const TABLE_END = "<!-- handler-table:end -->";
23
+
24
+ /**
25
+ * Render the handler table for JOB_NAMES, one row per job name.
26
+ * @param {Record<string, Function>} jobHandlers
27
+ * @returns {string} markdown table (marker lines excluded)
28
+ */
29
+ export function renderHandlerTable(jobHandlers) {
30
+ const rows = Object.values(JOB_NAMES).map((jobName) => {
31
+ const handler = jobHandlers[jobName];
32
+ const module = HANDLER_MODULES[jobName];
33
+ if (typeof handler !== "function" || !module) {
34
+ throw new AppError(
35
+ `No handler registration or README metadata for job: ${jobName}`,
36
+ 500,
37
+ "JOB_README_ROW_MISSING",
38
+ );
39
+ }
40
+ return `| \`${jobName}\` | \`${handler.name}\` (\`handlers/${module}\`) |`;
41
+ });
42
+ return ["| Job name | Handler |", "|---|---|", ...rows].join("\n");
43
+ }
44
+
45
+ /**
46
+ * Replace the marked table block in a README with the generated one.
47
+ * @param {string} markdown
48
+ * @param {Record<string, Function>} jobHandlers
49
+ * @returns {string} updated markdown
50
+ */
51
+ export function replaceHandlerTable(markdown, jobHandlers) {
52
+ const start = markdown.indexOf(TABLE_START);
53
+ const end = markdown.indexOf(TABLE_END);
54
+ if (start === -1 || end === -1 || end < start) {
55
+ throw new AppError(
56
+ "README is missing handler-table markers",
57
+ 500,
58
+ "JOB_README_MARKERS_MISSING",
59
+ );
60
+ }
61
+ return (
62
+ markdown.slice(0, start + TABLE_START.length) +
63
+ "\n" +
64
+ renderHandlerTable(jobHandlers) +
65
+ "\n" +
66
+ markdown.slice(end)
67
+ );
68
+ }