create-m5kdev 0.25.3 → 0.26.1

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 (143) hide show
  1. package/dist/src/create.cjs +18 -1
  2. package/dist/src/create.cjs.map +1 -1
  3. package/dist/src/fs.cjs +65 -5
  4. package/dist/src/fs.cjs.map +1 -1
  5. package/dist/src/fs.d.cts +13 -2
  6. package/dist/src/index.cjs +0 -0
  7. package/dist/src/paths.cjs +2 -2
  8. package/dist/src/prompts.cjs +22 -0
  9. package/dist/src/prompts.cjs.map +1 -1
  10. package/dist/src/runCli.cjs +13 -2
  11. package/dist/src/runCli.cjs.map +1 -1
  12. package/dist/src/types.d.cts +9 -1
  13. package/package.json +8 -6
  14. package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +46 -3
  15. package/templates/minimal-app/.cursor/rules/module-db-guide.mdc +15 -1
  16. package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -0
  17. package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +98 -193
  18. package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -173
  19. package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +133 -240
  20. package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -113
  21. package/templates/minimal-app/README.md.tpl +3 -3
  22. package/templates/minimal-app/apps/.gitignore.tpl +13 -0
  23. package/templates/minimal-app/apps/e2e/package.json.tpl +23 -0
  24. package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +39 -0
  25. package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -0
  26. package/templates/minimal-app/apps/e2e/servers.ts.tpl +100 -0
  27. package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -0
  28. package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -0
  29. package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -0
  30. package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -0
  31. package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -0
  32. package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -0
  33. package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -0
  34. package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -0
  35. package/templates/minimal-app/apps/email/package.json.tpl +34 -34
  36. package/templates/minimal-app/apps/email/src/emails/accountDeletionEmail.tsx.tpl +35 -34
  37. package/templates/minimal-app/apps/email/src/emails/organizationInviteEmail.tsx.tpl +34 -33
  38. package/templates/minimal-app/apps/email/src/emails/passwordResetEmail.tsx.tpl +35 -34
  39. package/templates/minimal-app/apps/email/src/emails/verificationEmail.tsx.tpl +37 -36
  40. package/templates/minimal-app/apps/email/src/emails/waitlistEmails.tsx.tpl +87 -0
  41. package/templates/minimal-app/apps/email/src/index.ts.tpl +72 -32
  42. package/templates/minimal-app/apps/email/src/resources.ts.tpl +65 -36
  43. package/templates/minimal-app/apps/email/tsconfig.json.tpl +16 -11
  44. package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -0
  45. package/templates/minimal-app/apps/expo/.prettierignore +13 -0
  46. package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -0
  47. package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -0
  48. package/templates/minimal-app/apps/expo/README.md.tpl +44 -0
  49. package/templates/minimal-app/apps/expo/app.json.tpl +36 -0
  50. package/templates/minimal-app/apps/expo/assets/images/adaptive-icon.png +0 -0
  51. package/templates/minimal-app/apps/expo/assets/images/favicon.png +0 -0
  52. package/templates/minimal-app/apps/expo/assets/images/icon.png +0 -0
  53. package/templates/minimal-app/apps/expo/assets/images/splash-icon.png +0 -0
  54. package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -0
  55. package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
  56. package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -0
  57. package/templates/minimal-app/apps/expo/package.json.tpl +59 -0
  58. package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -0
  59. package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -0
  60. package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -0
  61. package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -0
  62. package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -0
  63. package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -0
  64. package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -0
  65. package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -0
  66. package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -0
  67. package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -0
  68. package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -0
  69. package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -0
  70. package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -0
  71. package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -0
  72. package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -0
  73. package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -0
  74. package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -0
  75. package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -0
  76. package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -0
  77. package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
  78. package/templates/minimal-app/apps/server/AGENTS.md.tpl +39 -39
  79. package/templates/minimal-app/apps/server/drizzle/0000_wealthy_absorbing_man.sql +268 -0
  80. package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +26 -0
  81. package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +1890 -0
  82. package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +13 -0
  83. package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +33 -0
  84. package/templates/minimal-app/apps/server/drizzle/seed.e2e.ts.tpl +244 -0
  85. package/templates/minimal-app/apps/server/drizzle/seed.ts.tpl +181 -180
  86. package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +14 -21
  87. package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +33 -35
  88. package/templates/minimal-app/apps/server/package.json.tpl +56 -52
  89. package/templates/minimal-app/apps/server/src/app.ts.tpl +142 -126
  90. package/templates/minimal-app/apps/server/src/index.ts.tpl +58 -58
  91. package/templates/minimal-app/apps/server/src/lib/auth.ts.tpl +12 -12
  92. package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +25 -25
  93. package/templates/minimal-app/apps/server/src/modules/posts/posts.dto.ts.tpl +23 -0
  94. package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +30 -40
  95. package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +54 -54
  96. package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -112
  97. package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +104 -170
  98. package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -48
  99. package/templates/minimal-app/apps/server/src/modules/posts/posts.utils.ts.tpl +19 -0
  100. package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -0
  101. package/templates/minimal-app/apps/server/src/schema.ts.tpl +20 -0
  102. package/templates/minimal-app/apps/server/src/types.ts.tpl +1 -1
  103. package/templates/minimal-app/apps/server/tsconfig.json.tpl +20 -14
  104. package/templates/minimal-app/apps/server/tsup.config.ts.tpl +9 -9
  105. package/templates/minimal-app/apps/shared/package.json.tpl +24 -24
  106. package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -2
  107. package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
  108. package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
  109. package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -76
  110. package/templates/minimal-app/apps/shared/tsconfig.json.tpl +16 -12
  111. package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
  112. package/templates/minimal-app/apps/webapp/package.json.tpl +53 -52
  113. package/templates/minimal-app/apps/webapp/public/{push-sw.js → push-sw.js.tpl} +34 -34
  114. package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +44 -13
  115. package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +18 -12
  116. package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +33 -40
  117. package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -75
  118. package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
  119. package/templates/minimal-app/apps/webapp/src/modules/posts/PostsRoute.tsx.tpl +278 -307
  120. package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostCard.tsx.tpl +96 -138
  121. package/templates/minimal-app/apps/webapp/src/modules/posts/components/PostEditorModal.tsx.tpl +118 -124
  122. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostActions.ts.tpl +108 -116
  123. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsList.ts.tpl +15 -9
  124. package/templates/minimal-app/apps/webapp/src/modules/posts/hooks/usePostsRoute.ts.tpl +13 -13
  125. package/templates/minimal-app/apps/webapp/src/modules/posts/posts.utils.ts.tpl +16 -16
  126. package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
  127. package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -6
  128. package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
  129. package/templates/minimal-app/apps/webapp/translations/{app.json.tpl → en/app.json.tpl} +7 -7
  130. package/templates/minimal-app/apps/webapp/translations/en/{blog-app.json.tpl → {{APP_SLUG}}.json.tpl} +135 -135
  131. package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
  132. package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
  133. package/templates/minimal-app/template.manifest.json +21 -0
  134. package/templates/minimal-app/apps/server/drizzle/generate-schema.ts.tpl +0 -31
  135. package/templates/minimal-app/apps/server/src/db.ts.tpl +0 -7
  136. package/templates/minimal-app/apps/server/src/generated/schema.ts.tpl +0 -13
  137. package/templates/minimal-app/apps/server/src/utils/trpc.ts.tpl +0 -8
  138. package/templates/minimal-app/apps/shared/src/modules/app/locale.constants.ts.tpl +0 -7
  139. package/templates/minimal-app/apps/shared/src/modules/app/roles.constants.ts.tpl +0 -17
  140. package/templates/minimal-app/apps/webapp/src/Providers.tsx.tpl +0 -36
  141. package/templates/minimal-app/apps/webapp/src/modules/notification/PushNotificationsPanel.tsx.tpl +0 -89
  142. package/templates/minimal-app/apps/webapp/src/navigation/navigation.config.tsx.tpl +0 -85
  143. /package/templates/minimal-app/apps/webapp/public/{logo.svg → logo.svg.tpl} +0 -0
@@ -1,21 +1,14 @@
1
- import { spawnSync } from "node:child_process";
2
- import { generateSchema } from "./generate-schema";
3
-
4
- export async function syncDatabase(): Promise<void> {
5
- await generateSchema();
6
-
7
- const command = process.platform === "win32" ? "drizzle-kit.cmd" : "drizzle-kit";
8
- const result = spawnSync(command, ["push", "--config", "drizzle.config.ts", "--force"], {
9
- cwd: process.cwd(),
10
- env: process.env,
11
- stdio: "inherit",
12
- });
13
-
14
- if (result.status !== 0) {
15
- throw new Error(`drizzle-kit push failed with exit code ${result.status ?? "unknown"}`);
16
- }
17
- }
18
-
19
- syncDatabase().then(() => {
20
- console.info("Sync completed");
21
- });
1
+ import { isRemote, orm } from "./db";
2
+
3
+ async function sync() {
4
+ if (isRemote) {
5
+ await orm.$client.sync();
6
+ } else {
7
+ console.info("Local database no sync required");
8
+ }
9
+ }
10
+
11
+ sync().then(() => {
12
+ console.info("Sync completed");
13
+ process.exit(0);
14
+ });
@@ -1,35 +1,33 @@
1
- import * as dotenv from "dotenv";
2
- import { defineConfig } from "drizzle-kit";
3
-
4
- dotenv.config({ path: "../shared/.env" });
5
-
6
- const url = process.env.TURSO_DATABASE_URL || process.env.DATABASE_URL;
7
- const authToken = process.env.TURSO_AUTH_TOKEN;
8
- const schema = "./src/generated/schema.ts";
9
-
10
- if (!url) {
11
- throw new Error("DATABASE_URL or TURSO_DATABASE_URL must be set");
12
- }
13
-
14
- const isRemote = Boolean(process.env.TURSO_DATABASE_URL && authToken);
15
-
16
- export default defineConfig(
17
- isRemote
18
- ? {
19
- dialect: "turso",
20
- schema,
21
- out: "./drizzle",
22
- dbCredentials: {
23
- url,
24
- authToken,
25
- },
26
- }
27
- : {
28
- dialect: "sqlite",
29
- schema,
30
- out: "./drizzle",
31
- dbCredentials: {
32
- url,
33
- },
34
- }
35
- );
1
+ import * as dotenv from "dotenv";
2
+ import { defineConfig } from "drizzle-kit";
3
+
4
+ dotenv.config({ path: process.env.DRIZZLE_ENV_PATH || "../shared/.env" });
5
+
6
+ const url = process.env.TURSO_DATABASE_URL || process.env.DATABASE_URL;
7
+ const authToken = process.env.TURSO_AUTH_TOKEN;
8
+ const schema = process.env.DRIZZLE_SCHEMA_PATH || "./src/schema.ts";
9
+
10
+ if (!url) {
11
+ throw new Error("DATABASE_URL or TURSO_DATABASE_URL must be set");
12
+ }
13
+
14
+ export default defineConfig(
15
+ url && authToken
16
+ ? {
17
+ dialect: "turso",
18
+ schema,
19
+ out: "./drizzle",
20
+ dbCredentials: {
21
+ url,
22
+ authToken,
23
+ },
24
+ }
25
+ : {
26
+ dialect: "sqlite",
27
+ schema,
28
+ out: "./drizzle",
29
+ dbCredentials: {
30
+ url,
31
+ },
32
+ }
33
+ );
@@ -1,52 +1,56 @@
1
- {
2
- "name": "{{PACKAGE_SCOPE}}/server",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "dist/index.js",
6
- "scripts": {
7
- "dev": "tsx watch --env-file=../shared/.env src/index.ts",
8
- "build": "tsup",
9
- "start": "node dist/index.js",
10
- "lint": "biome check .",
11
- "lint:fix": "biome check . --write",
12
- "check-types": "tsc --noEmit",
13
- "generate:schema": "tsx --env-file=../shared/.env drizzle/generate-schema.ts",
14
- "sync": "tsx --env-file=../shared/.env drizzle/sync.ts",
15
- "seed": "tsx --env-file=../shared/.env drizzle/seed.ts"
16
- },
17
- "dependencies": {
18
- "{{PACKAGE_SCOPE}}/email": "workspace:*",
19
- "{{PACKAGE_SCOPE}}/shared": "workspace:*",
20
- "@libsql/client": "catalog:",
21
- "@m5kdev/backend": "catalog:",
22
- "@m5kdev/commons": "catalog:",
23
- "@trpc/server": "catalog:",
24
- "better-auth": "catalog:",
25
- "cors": "catalog:",
26
- "dotenv": "catalog:",
27
- "drizzle-orm": "catalog:",
28
- "express": "catalog:",
29
- "ioredis": "catalog:",
30
- "neverthrow": "catalog:",
31
- "react": "catalog:",
32
- "react-dom": "catalog:",
33
- "uuid": "catalog:",
34
- "zod": "catalog:"
35
- },
36
- "devDependencies": {
37
- "@m5kdev/config": "catalog:",
38
- "@types/cors": "catalog:",
39
- "@types/express": "catalog:",
40
- "@types/node": "catalog:",
41
- "@types/react": "catalog:",
42
- "@types/react-dom": "catalog:",
43
- "drizzle-kit": "catalog:",
44
- "tslib": "catalog:",
45
- "tsup": "catalog:",
46
- "tsx": "catalog:",
47
- "typescript": "catalog:"
48
- },
49
- "exports": {
50
- "./types": "./src/types.ts"
51
- }
52
- }
1
+ {
2
+ "name": "{{PACKAGE_SCOPE}}/server",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "tsx watch --env-file=../shared/.env src/index.ts",
8
+ "build": "tsup",
9
+ "start": "node dist/index.js",
10
+ "lint": "biome check .",
11
+ "lint:fix": "biome check . --write",
12
+ "check-types": "tsc --noEmit",
13
+ "drizzle:generate": "drizzle-kit generate",
14
+ "drizzle:migrate": "drizzle-kit migrate",
15
+ "drizzle:studio": "drizzle-kit studio",
16
+ "drizzle:sync": "tsx --env-file=../shared/.env drizzle/sync.ts",
17
+ "drizzle:seed": "tsx --env-file=../shared/.env drizzle/seed.ts",
18
+ "drizzle:reset": "tsx --env-file=../shared/.env drizzle/reset.ts"
19
+ },
20
+ "dependencies": {
21
+ "{{PACKAGE_SCOPE}}/email": "workspace:*",
22
+ "{{PACKAGE_SCOPE}}/shared": "workspace:*",
23
+ "@libsql/client": "catalog:",
24
+ "@m5kdev/backend": "catalog:",
25
+ "@m5kdev/commons": "catalog:",
26
+ "@trpc/server": "catalog:",
27
+ "better-auth": "catalog:",
28
+ "cors": "catalog:",
29
+ "dotenv": "catalog:",
30
+ "drizzle-orm": "catalog:",
31
+ "drizzle-zod": "catalog:",
32
+ "express": "catalog:",
33
+ "ioredis": "catalog:",
34
+ "neverthrow": "catalog:",
35
+ "react": "catalog:",
36
+ "react-dom": "catalog:",
37
+ "uuid": "catalog:",
38
+ "zod": "catalog:"
39
+ },
40
+ "devDependencies": {
41
+ "@m5kdev/config": "catalog:",
42
+ "@types/cors": "catalog:",
43
+ "@types/express": "catalog:",
44
+ "@types/node": "catalog:",
45
+ "@types/react": "catalog:",
46
+ "@types/react-dom": "catalog:",
47
+ "drizzle-kit": "catalog:",
48
+ "tslib": "catalog:",
49
+ "tsup": "catalog:",
50
+ "tsx": "catalog:",
51
+ "typescript": "catalog:"
52
+ },
53
+ "exports": {
54
+ "./types": "./src/types.ts"
55
+ }
56
+ }
@@ -1,126 +1,142 @@
1
- import { createBackendApp } from "@m5kdev/backend/app";
2
- import { createBetterAuth } from "@m5kdev/backend/modules/auth/auth.lib";
3
- import { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
4
- import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
5
- import { NotificationModule } from "@m5kdev/backend/modules/notification/notification.module";
6
- import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
7
- import { templates } from "{{PACKAGE_SCOPE}}/email";
8
- import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
9
- import { APP_NAME } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
10
- import { AUTH_LOCALE_CONFIG } from "{{PACKAGE_SCOPE}}/shared/modules/app/locale.constants";
11
- import { APP_ROLES_CONFIG } from "{{PACKAGE_SCOPE}}/shared/modules/app/roles.constants";
12
- import { USER_LOCALE_HEADER } from "@m5kdev/commons/modules/auth/auth.constants";
13
- import cors from "cors";
14
- import express from "express";
15
- import { schema } from "./generated/schema";
16
- import { PostsModule } from "./modules/posts/posts.module";
17
-
18
- const app = express();
19
- const appUrl = process.env.VITE_APP_URL ?? "http://localhost:5173";
20
- const serverUrl = process.env.VITE_SERVER_URL ?? "http://localhost:8080";
21
- const databaseUrl = process.env.DATABASE_URL ?? "file:./local.db";
22
- const syncUrl = process.env.TURSO_DATABASE_URL;
23
- const authToken = process.env.TURSO_AUTH_TOKEN;
24
- const redisUrl = process.env.REDIS_URL ?? "redis://127.0.0.1:6379";
25
- const resendApiKey = process.env.RESEND_API_KEY;
26
- const enableWaitlist = process.env.VITE_ENABLE_WAITLIST === "true";
27
-
28
- const connection =
29
- syncUrl && authToken
30
- ? {
31
- url: databaseUrl,
32
- syncUrl,
33
- authToken,
34
- syncInterval: 60,
35
- readYourWrites: true,
36
- }
37
- : {
38
- url: databaseUrl,
39
- };
40
-
41
- app.use(express.json());
42
- app.use(
43
- cors({
44
- origin: [appUrl],
45
- credentials: true,
46
- methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
47
- allowedHeaders: [
48
- "Content-Type",
49
- "Authorization",
50
- "Waitlist-Invitation-Code",
51
- "Organization-Invitation-Code",
52
- "Admin-Create-Verified-User",
53
- USER_LOCALE_HEADER,
54
- ],
55
- })
56
- );
57
-
58
- export const builtBackendApp = createBackendApp(
59
- {
60
- db: connection,
61
- express: app,
62
- schema: schema as never,
63
- app: {
64
- name: APP_NAME,
65
- urls: {
66
- web: appUrl,
67
- api: serverUrl,
68
- },
69
- locales: AUTH_LOCALE_CONFIG,
70
- roles: APP_ROLES_CONFIG,
71
- },
72
- i18n: {
73
- resources: emailResources,
74
- },
75
- redis: {
76
- url: redisUrl,
77
- options: { maxRetriesPerRequest: null },
78
- },
79
- resend: resendApiKey ? { apiKey: resendApiKey } : undefined,
80
- email: {
81
- mode: resendApiKey ? "send" : "store",
82
- from: "no-reply@local.m5kdev.test",
83
- systemNotificationEmail: "ops@local.m5kdev.test",
84
- outputDirectory: ".emails",
85
- },
86
- auth: {
87
- factory({ db, services, appConfig, i18n }) {
88
- return createBetterAuth({
89
- orm: db.orm as never,
90
- schema: db.schema as never,
91
- services: {
92
- email: services.email.email,
93
- },
94
- app: appConfig,
95
- i18n,
96
- config: {
97
- waitlist: enableWaitlist,
98
- provisionedAccountEmailDomain: "provisioned.{{APP_SLUG}}.local",
99
- },
100
- options: {
101
- secret: process.env.BETTER_AUTH_SECRET ?? "QJvCmK9UBuHAWbNr2vV3ROVt8XrBYV5d",
102
- },
103
- });
104
- },
105
- },
106
- },
107
- [
108
- new EmailModule(templates as never),
109
- new AuthModule(),
110
- new WorkflowModule({
111
- queues: {
112
- fast: { concurrency: 5 },
113
- },
114
- defaultQueue: "fast",
115
- defaults: {
116
- timeout: 60_000,
117
- jobOptions: { removeOnComplete: { age: 3600 } },
118
- },
119
- }),
120
- new NotificationModule(),
121
- new PostsModule(),
122
- ] as const
123
- );
124
-
125
- export const appRouter = builtBackendApp.trpc.router;
126
- export type AppRouter = typeof appRouter;
1
+ import { createBackendApp } from "@m5kdev/backend/app";
2
+ import { createBetterAuth } from "@m5kdev/backend/modules/auth/auth.lib";
3
+ import { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
4
+ import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
5
+ import { EmailPreviewModule } from "@m5kdev/backend/modules/email/email.preview.module";
6
+ import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
7
+ import { USER_LOCALE_HEADER } from "@m5kdev/commons/modules/auth/auth.constants";
8
+ import { templates } from "{{PACKAGE_SCOPE}}/email";
9
+ import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
10
+ import {
11
+ APP_LOCALE_CONFIG,
12
+ APP_NAME,
13
+ APP_ROLES_CONFIG,
14
+ } from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
15
+ import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
16
+ import cors from "cors";
17
+ import express from "express";
18
+ import { PostsModule } from "./modules/posts/posts.module";
19
+ // m5k:test-harness:start
20
+ import { TestHarnessModule } from "./modules/test-harness/test-harness.module";
21
+ import * as schema from "./schema";
22
+
23
+ // m5k:test-harness:end
24
+
25
+ const app = express();
26
+ const appUrl = process.env.VITE_APP_URL ?? "http://localhost:5173";
27
+ const serverUrl = process.env.VITE_SERVER_URL ?? "http://localhost:8080";
28
+ const databaseUrl = process.env.DATABASE_URL ?? "file:./local.db";
29
+ const syncUrl = process.env.TURSO_DATABASE_URL;
30
+ const authToken = process.env.TURSO_AUTH_TOKEN;
31
+ const redisUrl = process.env.REDIS_URL ?? "redis://127.0.0.1:6379";
32
+ const resendApiKey = process.env.RESEND_API_KEY;
33
+ const enableWaitlist = process.env.VITE_ENABLE_WAITLIST === "true";
34
+
35
+ const connection =
36
+ syncUrl && authToken
37
+ ? {
38
+ url: databaseUrl,
39
+ syncUrl,
40
+ authToken,
41
+ syncInterval: 60,
42
+ readYourWrites: true,
43
+ }
44
+ : {
45
+ url: databaseUrl,
46
+ };
47
+
48
+ app.use(express.json());
49
+ app.use(
50
+ cors({
51
+ origin: [appUrl],
52
+ credentials: true,
53
+ methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
54
+ allowedHeaders: [
55
+ "Content-Type",
56
+ "Authorization",
57
+ "Waitlist-Invitation-Code",
58
+ "Organization-Invitation-Code",
59
+ "Admin-Create-Verified-User",
60
+ USER_LOCALE_HEADER,
61
+ ],
62
+ })
63
+ );
64
+
65
+ export const builtBackendApp = createBackendApp(
66
+ {
67
+ db: connection,
68
+ express: app,
69
+ schema,
70
+ app: {
71
+ name: APP_NAME,
72
+ urls: {
73
+ web: appUrl,
74
+ api: serverUrl,
75
+ },
76
+ locales: APP_LOCALE_CONFIG,
77
+ roles: APP_ROLES_CONFIG,
78
+ },
79
+ i18n: {
80
+ resources: emailResources,
81
+ },
82
+ redis: {
83
+ url: redisUrl,
84
+ options: { maxRetriesPerRequest: null },
85
+ },
86
+ resend: resendApiKey ? { apiKey: resendApiKey } : undefined,
87
+ email: {
88
+ mode: resendApiKey ? "send" : "store",
89
+ from: process.env.EMAIL_FROM ?? "no-reply@local.m5kdev.test",
90
+ systemNotificationEmail: process.env.SYSTEM_NOTIFICATION_EMAIL ?? "ops@local.m5kdev.test",
91
+ outputDirectory: process.env.EMAIL_OUTPUT_DIRECTORY ?? ".emails",
92
+ },
93
+ auth: {
94
+ factory({ db, services, appConfig, i18n }) {
95
+ return createBetterAuth({
96
+ orm: db.orm,
97
+ schema: db.schema,
98
+ services: {
99
+ email: services.email.email,
100
+ },
101
+ app: appConfig,
102
+ i18n,
103
+ config: {
104
+ waitlist: enableWaitlist,
105
+ provisionedAccountEmailDomain:
106
+ process.env.PROVISIONED_EMAIL_DOMAIN ?? "provisioned.{{APP_SLUG}}.local",
107
+ },
108
+ options: {
109
+ secret: process.env.BETTER_AUTH_SECRET ?? "QJvCmK9UBuHAWbNr2vV3ROVt8XrBYV5d",
110
+ },
111
+ });
112
+ },
113
+ },
114
+ },
115
+ [
116
+ new EmailModule(templates as never),
117
+ new AuthModule(),
118
+ new WorkflowModule({
119
+ queues: {
120
+ fast: { concurrency: 5 },
121
+ },
122
+ defaultQueue: "fast",
123
+ defaults: {
124
+ timeout: 60_000,
125
+ jobOptions: { removeOnComplete: { age: 3600 } },
126
+ },
127
+ reconcile: { enabled: true },
128
+ }),
129
+ new PostsModule(),
130
+ new EmailPreviewModule({ allowDelete: process.env.NODE_ENV !== "production" }),
131
+ // m5k:test-harness:start
132
+ new TestHarnessModule(),
133
+ // m5k:test-harness:end
134
+ ] as const
135
+ );
136
+
137
+ export type AppRouter = typeof builtBackendApp.trpc.router;
138
+ export type RouterInputs = inferRouterInputs<AppRouter>;
139
+ export type RouterOutputs = inferRouterOutputs<AppRouter>;
140
+
141
+ export type Orm = typeof builtBackendApp.db.orm;
142
+ export type Schema = typeof builtBackendApp.db.schema;
@@ -1,58 +1,58 @@
1
- import type { Server } from "node:http";
2
- import { builtBackendApp } from "./app";
3
-
4
- const port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 8080;
5
-
6
- let httpServer: Server | undefined;
7
-
8
- function logError(context: string, error: unknown): void {
9
- console.error(`[server] ${context}`, error);
10
- }
11
-
12
- async function shutdown(): Promise<void> {
13
- try {
14
- await builtBackendApp.shutdown();
15
- } catch (e) {
16
- logError("builtBackendApp.shutdown() failed", e);
17
- }
18
- if (httpServer) {
19
- await new Promise<void>((resolve) => {
20
- httpServer?.close((err) => {
21
- if (err) {
22
- logError("HTTP server close failed", err);
23
- }
24
- resolve();
25
- });
26
- });
27
- }
28
- process.exit(0);
29
- }
30
-
31
- process.once("SIGINT", () => {
32
- void shutdown();
33
- });
34
- process.once("SIGTERM", () => {
35
- void shutdown();
36
- });
37
-
38
- async function start(): Promise<void> {
39
- await builtBackendApp.start();
40
- await new Promise<void>((resolve, reject) => {
41
- httpServer = builtBackendApp.express.app.listen(port, () => {
42
- console.info(
43
- `Server running at ${process.env.VITE_SERVER_URL ?? `http://localhost:${port}`}`
44
- );
45
- resolve();
46
- });
47
- httpServer.on("error", reject);
48
- });
49
- }
50
-
51
- void (async () => {
52
- try {
53
- await start();
54
- } catch (e) {
55
- logError("Fatal: builtBackendApp.start() or HTTP listen failed", e);
56
- process.exit(1);
57
- }
58
- })();
1
+ import type { Server } from "node:http";
2
+ import { builtBackendApp } from "./app";
3
+
4
+ const port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 8080;
5
+
6
+ let httpServer: Server | undefined;
7
+
8
+ function logError(context: string, error: unknown): void {
9
+ console.error(`[server] ${context}`, error);
10
+ }
11
+
12
+ async function shutdown(): Promise<void> {
13
+ try {
14
+ await builtBackendApp.shutdown();
15
+ } catch (e) {
16
+ logError("builtBackendApp.shutdown() failed", e);
17
+ }
18
+ if (httpServer) {
19
+ await new Promise<void>((resolve) => {
20
+ httpServer?.close((err) => {
21
+ if (err) {
22
+ logError("HTTP server close failed", err);
23
+ }
24
+ resolve();
25
+ });
26
+ });
27
+ }
28
+ process.exit(0);
29
+ }
30
+
31
+ process.once("SIGINT", () => {
32
+ void shutdown();
33
+ });
34
+ process.once("SIGTERM", () => {
35
+ void shutdown();
36
+ });
37
+
38
+ async function start(): Promise<void> {
39
+ await builtBackendApp.start();
40
+ await new Promise<void>((resolve, reject) => {
41
+ httpServer = builtBackendApp.express.app.listen(port, () => {
42
+ console.info(
43
+ `Server running at ${process.env.VITE_SERVER_URL ?? `http://localhost:${port}`}`
44
+ );
45
+ resolve();
46
+ });
47
+ httpServer.on("error", reject);
48
+ });
49
+ }
50
+
51
+ void (async () => {
52
+ try {
53
+ await start();
54
+ } catch (e) {
55
+ logError("Fatal: builtBackendApp.start() or HTTP listen failed", e);
56
+ process.exit(1);
57
+ }
58
+ })();
@@ -1,12 +1,12 @@
1
- import { builtBackendApp } from "../app";
2
-
3
- const authInstance = builtBackendApp.auth?.instance;
4
-
5
- if (!authInstance) {
6
- throw new Error("Auth is not configured on the backend app.");
7
- }
8
-
9
- export const auth = authInstance;
10
-
11
- export type Session = typeof auth.$Infer.Session;
12
- export type User = Session["user"];
1
+ import { builtBackendApp } from "../app";
2
+
3
+ const authInstance = builtBackendApp.auth?.instance;
4
+
5
+ if (!authInstance) {
6
+ throw new Error("Auth is not configured on the backend app.");
7
+ }
8
+
9
+ export const auth = authInstance;
10
+
11
+ export type Session = typeof auth.$Infer.Session;
12
+ export type User = Session["user"];