create-m5kdev 0.33.0 → 0.35.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/dist/src/catalog.cjs +21 -1
- package/dist/src/catalog.cjs.map +1 -1
- package/dist/src/catalog.d.cts +3 -1
- package/dist/src/create.cjs +6 -2
- package/dist/src/create.cjs.map +1 -1
- package/dist/src/doctor.cjs.map +1 -1
- package/dist/src/fs.cjs +10 -5
- package/dist/src/fs.cjs.map +1 -1
- package/dist/src/prompts.cjs +32 -0
- package/dist/src/prompts.cjs.map +1 -1
- package/dist/src/prompts.d.cts +7 -1
- package/dist/src/template.cjs +23 -4
- package/dist/src/template.cjs.map +1 -1
- package/dist/src/template.d.cts +17 -3
- package/dist/src/types.d.cts +11 -4
- package/package.json +4 -1
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +1 -0
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +1 -1
- package/templates/minimal-app/.cursor/rules/module-grants-guide.mdc +3 -3
- package/templates/minimal-app/.cursor/rules/module-module-guide.mdc +3 -2
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +1 -1
- package/templates/minimal-app/.cursor/rules/server-event-emit.mdc +41 -0
- package/templates/minimal-app/.dockerignore.tpl +31 -0
- package/templates/minimal-app/.gitignore.tpl +1 -0
- package/templates/minimal-app/AGENTS.md.tpl +14 -10
- package/templates/minimal-app/CLAUDE.md.tpl +133 -131
- package/templates/minimal-app/README.md.tpl +51 -19
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
- package/templates/minimal-app/apps/e2e/playwright.config.ts.tpl +51 -39
- package/templates/minimal-app/apps/e2e/playwright.expo.config.ts.tpl +38 -38
- package/templates/minimal-app/apps/e2e/servers.ts.tpl +136 -112
- package/templates/minimal-app/apps/e2e/tests/conversation.spec.ts.tpl +22 -0
- package/templates/minimal-app/apps/e2e/tests/email.spec.ts.tpl +47 -47
- package/templates/minimal-app/apps/e2e/tests/expo-helpers.ts.tpl +73 -73
- package/templates/minimal-app/apps/e2e/tests/expo-standard.spec.ts.tpl +517 -517
- package/templates/minimal-app/apps/e2e/tests/expo-waitlist.spec.ts.tpl +94 -94
- package/templates/minimal-app/apps/e2e/tests/files.spec.ts.tpl +35 -0
- package/templates/minimal-app/apps/e2e/tests/helpers.ts.tpl +289 -289
- package/templates/minimal-app/apps/e2e/tests/standard.spec.ts.tpl +516 -516
- package/templates/minimal-app/apps/e2e/tests/waitlist.spec.ts.tpl +107 -107
- package/templates/minimal-app/apps/e2e/tests/workflow.spec.ts.tpl +26 -0
- package/templates/minimal-app/apps/e2e/tsconfig.json.tpl +10 -10
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/email/src/emails/trialEndingEmail.tsx.tpl +40 -0
- package/templates/minimal-app/apps/email/src/index.ts.tpl +7 -0
- package/templates/minimal-app/apps/email/src/resources.ts.tpl +72 -65
- package/templates/minimal-app/apps/expo/app.json.tpl +36 -36
- package/templates/minimal-app/apps/expo/eslint.config.js.tpl +8 -8
- package/templates/minimal-app/apps/expo/metro.config.js.tpl +16 -16
- package/templates/minimal-app/apps/expo/src/app/_layout.tsx.tpl +31 -31
- package/templates/minimal-app/apps/expo/src/app/claim-account.tsx.tpl +151 -151
- package/templates/minimal-app/apps/expo/src/app/forgot-password.tsx.tpl +53 -53
- package/templates/minimal-app/apps/expo/src/app/index.tsx.tpl +8 -8
- package/templates/minimal-app/apps/expo/src/app/login.tsx.tpl +71 -71
- package/templates/minimal-app/apps/expo/src/app/logout.tsx.tpl +19 -19
- package/templates/minimal-app/apps/expo/src/app/organization/accept-invitation.tsx.tpl +85 -85
- package/templates/minimal-app/apps/expo/src/app/posts.tsx.tpl +40 -40
- package/templates/minimal-app/apps/expo/src/app/reset-password.tsx.tpl +75 -75
- package/templates/minimal-app/apps/expo/src/app/signup.tsx.tpl +193 -193
- package/templates/minimal-app/apps/expo/src/components/AuthScaffold.tsx.tpl +34 -34
- package/templates/minimal-app/apps/expo/src/components/FormTextField.tsx.tpl +37 -37
- package/templates/minimal-app/apps/expo/src/components/LoadingScreen.tsx.tpl +10 -10
- package/templates/minimal-app/apps/expo/src/components/StatusText.tsx.tpl +22 -22
- package/templates/minimal-app/apps/expo/src/config.ts.tpl +9 -9
- package/templates/minimal-app/apps/expo/src/global.css.tpl +11 -11
- package/templates/minimal-app/apps/expo/src/lib/trpc.ts.tpl +20 -20
- package/templates/minimal-app/apps/expo/tsconfig.json.tpl +11 -11
- package/templates/minimal-app/apps/expo/uniwind-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/landing/.env.production.example.tpl +7 -0
- package/templates/minimal-app/apps/landing/Dockerfile.tpl +39 -0
- package/templates/minimal-app/apps/landing/fly.toml.tpl +20 -0
- package/templates/minimal-app/apps/landing/index.html.tpl +35 -0
- package/templates/minimal-app/apps/landing/package.json.tpl +34 -0
- package/templates/minimal-app/apps/landing/src/App.tsx.tpl +12 -0
- package/templates/minimal-app/apps/landing/src/LandingPage.tsx.tpl +37 -0
- package/templates/minimal-app/apps/landing/src/index.css.tpl +24 -0
- package/templates/minimal-app/apps/landing/src/main.tsx.tpl +16 -0
- package/templates/minimal-app/apps/landing/src/vite-env.d.ts.tpl +11 -0
- package/templates/minimal-app/apps/landing/tsconfig.json.tpl +12 -0
- package/templates/minimal-app/apps/landing/vite.config.ts.tpl +25 -0
- package/templates/minimal-app/apps/server/AGENTS.md.tpl +55 -46
- package/templates/minimal-app/apps/server/{drizzle/seed.e2e.ts.tpl → db.e2e.ts.tpl} +57 -40
- package/templates/minimal-app/apps/server/{drizzle/seed.ts.tpl → db.ts.tpl} +27 -20
- package/templates/minimal-app/apps/server/drizzle/0003_silly_the_liberteens.sql +27 -0
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +56 -166
- package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +1796 -1910
- package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +1818 -1932
- package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2137 -0
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +8 -1
- package/templates/minimal-app/apps/server/drizzle.config.ts.tpl +3 -33
- package/templates/minimal-app/apps/server/package.json.tpl +5 -3
- package/templates/minimal-app/apps/server/src/app.ts.tpl +36 -0
- package/templates/minimal-app/apps/server/src/modules/conversation/conversation.mastra.ts.tpl +26 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl +51 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl +51 -0
- package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.trpc.ts.tpl +16 -0
- package/templates/minimal-app/apps/server/src/modules/posts/posts.db.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.grants.ts.tpl +28 -28
- package/templates/minimal-app/apps/server/src/modules/posts/posts.module.ts.tpl +57 -54
- package/templates/minimal-app/apps/server/src/modules/posts/posts.repository.ts.tpl +67 -67
- package/templates/minimal-app/apps/server/src/modules/posts/posts.service.ts.tpl +116 -116
- package/templates/minimal-app/apps/server/src/modules/posts/posts.trpc.ts.tpl +37 -37
- package/templates/minimal-app/apps/server/src/modules/test-harness/test-harness.module.ts.tpl +279 -279
- package/templates/minimal-app/apps/server/src/schema.ts.tpl +31 -20
- package/templates/minimal-app/apps/server/src/types.ts.tpl +12 -12
- package/templates/minimal-app/apps/server/tsconfig.json.tpl +1 -1
- package/templates/minimal-app/apps/server/tsup.config.ts.tpl +23 -9
- package/templates/minimal-app/apps/shared/.env.example.tpl +5 -0
- package/templates/minimal-app/apps/shared/.env.production.example.tpl +23 -0
- package/templates/minimal-app/apps/shared/.env.tpl +5 -0
- package/templates/minimal-app/apps/shared/Dockerfile.tpl +48 -0
- package/templates/minimal-app/apps/shared/fly.toml.tpl +24 -0
- package/templates/minimal-app/apps/shared/package.json.tpl +26 -24
- package/templates/minimal-app/apps/shared/src/modules/app/app.constants.ts.tpl +28 -28
- package/templates/minimal-app/apps/shared/src/modules/conversation/conversation.constants.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/src/modules/demo-workflow/demo-workflow.constants.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/src/modules/notification/notification.schema.ts.tpl +3 -3
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.constants.ts.tpl +5 -5
- package/templates/minimal-app/apps/shared/src/modules/posts/posts.schema.ts.tpl +25 -25
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
- package/templates/minimal-app/apps/webapp/public/push-sw.js.tpl +34 -34
- package/templates/minimal-app/apps/webapp/src/App.tsx.tpl +49 -44
- package/templates/minimal-app/apps/webapp/src/Layout.tsx.tpl +96 -65
- package/templates/minimal-app/apps/webapp/src/Router.tsx.tpl +114 -93
- package/templates/minimal-app/apps/webapp/src/StarterServerEventProvider.tsx.tpl +38 -0
- package/templates/minimal-app/apps/webapp/src/index.css.tpl +15 -15
- package/templates/minimal-app/apps/webapp/src/main.tsx.tpl +26 -26
- package/templates/minimal-app/apps/webapp/src/modules/conversation/ConversationRoute.tsx.tpl +36 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/FilesRoute.tsx.tpl +88 -0
- package/templates/minimal-app/apps/webapp/src/modules/files/hooks/useFilesRoute.ts.tpl +50 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/WorkflowsRoute.tsx.tpl +97 -0
- package/templates/minimal-app/apps/webapp/src/modules/workflows/hooks/useWorkflowsRoute.ts.tpl +64 -0
- package/templates/minimal-app/apps/webapp/src/utils/i18n.ts.tpl +13 -13
- package/templates/minimal-app/apps/webapp/src/utils/trpc.ts.tpl +10 -10
- package/templates/minimal-app/apps/webapp/src/vite-env.d.ts.tpl +1 -1
- package/templates/minimal-app/apps/webapp/translations/en/app.json.tpl +7 -7
- package/templates/minimal-app/apps/webapp/translations/en/{{APP_SLUG}}.json.tpl +193 -135
- package/templates/minimal-app/apps/webapp/tsconfig.json.tpl +17 -17
- package/templates/minimal-app/apps/webapp/vite.config.ts.tpl +31 -31
- package/templates/minimal-app/biome.json.tpl +17 -12
- package/templates/minimal-app/package.json.tpl +7 -2
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +14 -7
- package/templates/minimal-app/template.manifest.json +93 -2
- package/templates/minimal-app/apps/server/drizzle/db.ts.tpl +0 -26
- package/templates/minimal-app/apps/server/drizzle/guard.ts.tpl +0 -36
- package/templates/minimal-app/apps/server/drizzle/reset.ts.tpl +0 -40
- package/templates/minimal-app/apps/server/drizzle/sync.ts.tpl +0 -16
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
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
|
+
import { defineDrizzleKitConfig } from "@m5kdev/backend/db";
|
|
2
|
+
|
|
3
|
+
export default defineDrizzleKitConfig({ schema: "./src/schema.ts" });
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"drizzle:generate": "drizzle-kit generate",
|
|
14
14
|
"drizzle:migrate": "drizzle-kit migrate",
|
|
15
15
|
"drizzle:studio": "drizzle-kit studio",
|
|
16
|
-
"drizzle:sync": "tsx --env-file=../shared/.env
|
|
17
|
-
"drizzle:seed": "tsx --env-file=../shared/.env
|
|
18
|
-
"drizzle:reset": "tsx --env-file=../shared/.env
|
|
16
|
+
"drizzle:sync": "tsx --env-file=../shared/.env db.ts sync",
|
|
17
|
+
"drizzle:seed": "tsx --env-file=../shared/.env db.ts seed",
|
|
18
|
+
"drizzle:reset": "tsx --env-file=../shared/.env db.ts reset"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"{{PACKAGE_SCOPE}}/email": "workspace:*",
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"@libsql/client": "catalog:m5kdev",
|
|
24
24
|
"@m5kdev/backend": "catalog:m5kdev",
|
|
25
25
|
"@m5kdev/commons": "catalog:m5kdev",
|
|
26
|
+
"@mastra/core": "catalog:m5kdev",
|
|
27
|
+
"@openrouter/ai-sdk-provider": "catalog:m5kdev",
|
|
26
28
|
"@opentelemetry/api": "catalog:m5kdev",
|
|
27
29
|
"@trpc/server": "catalog:m5kdev",
|
|
28
30
|
"better-auth": "catalog:m5kdev",
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { createBackendApp } from "@m5kdev/backend/app";
|
|
2
2
|
import { shutdownTelemetry } from "@m5kdev/backend/lib/otel";
|
|
3
|
+
// m5k:ai:start
|
|
4
|
+
import { AIModule } from "@m5kdev/backend/modules/ai/ai.module";
|
|
5
|
+
// m5k:ai:end
|
|
3
6
|
import { createBetterAuth } from "@m5kdev/backend/modules/auth/auth.lib";
|
|
4
7
|
import { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
|
|
5
8
|
import { EmailModule } from "@m5kdev/backend/modules/email/email.module";
|
|
6
9
|
import { EmailPreviewModule } from "@m5kdev/backend/modules/email/email.preview.module";
|
|
10
|
+
// m5k:files:start
|
|
11
|
+
import { FileModule } from "@m5kdev/backend/modules/file/file.module";
|
|
12
|
+
// m5k:files:end
|
|
13
|
+
// m5k:notifications:start
|
|
14
|
+
import { NotificationModule } from "@m5kdev/backend/modules/notification/notification.module";
|
|
15
|
+
// m5k:notifications:end
|
|
16
|
+
// m5k:workflows:start
|
|
7
17
|
import { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
|
|
18
|
+
// m5k:workflows:end
|
|
8
19
|
import { templates } from "{{PACKAGE_SCOPE}}/email";
|
|
9
20
|
import { emailResources } from "{{PACKAGE_SCOPE}}/email/resources";
|
|
10
21
|
import {
|
|
@@ -12,6 +23,12 @@ import {
|
|
|
12
23
|
APP_NAME,
|
|
13
24
|
APP_ROLES_CONFIG,
|
|
14
25
|
} from "{{PACKAGE_SCOPE}}/shared/modules/app/app.constants";
|
|
26
|
+
// m5k:ai:start
|
|
27
|
+
import { mastra, openrouter } from "./modules/conversation/conversation.mastra";
|
|
28
|
+
// m5k:ai:end
|
|
29
|
+
// m5k:workflows:start
|
|
30
|
+
import { DemoWorkflowModule } from "./modules/demo-workflow/demo-workflow.module";
|
|
31
|
+
// m5k:workflows:end
|
|
15
32
|
import { PostsModule } from "./modules/posts/posts.module";
|
|
16
33
|
// m5k:test-harness:start
|
|
17
34
|
import { TestHarnessModule } from "./modules/test-harness/test-harness.module";
|
|
@@ -23,7 +40,9 @@ const serverUrl = process.env.VITE_SERVER_URL ?? "http://localhost:8080";
|
|
|
23
40
|
const databaseUrl = process.env.DATABASE_URL ?? "file:./local.db";
|
|
24
41
|
const syncUrl = process.env.TURSO_DATABASE_URL;
|
|
25
42
|
const authToken = process.env.TURSO_AUTH_TOKEN;
|
|
43
|
+
// m5k:workflows:start
|
|
26
44
|
const redisUrl = process.env.REDIS_URL ?? "redis://127.0.0.1:6379";
|
|
45
|
+
// m5k:workflows:end
|
|
27
46
|
const resendApiKey = process.env.RESEND_API_KEY;
|
|
28
47
|
const enableWaitlist = process.env.VITE_ENABLE_WAITLIST === "true";
|
|
29
48
|
|
|
@@ -57,10 +76,13 @@ export const builtBackendApp = createBackendApp(
|
|
|
57
76
|
i18n: {
|
|
58
77
|
resources: emailResources,
|
|
59
78
|
},
|
|
79
|
+
spa: { root: "./client" },
|
|
80
|
+
// m5k:workflows:start
|
|
60
81
|
redis: {
|
|
61
82
|
url: redisUrl,
|
|
62
83
|
options: { maxRetriesPerRequest: null },
|
|
63
84
|
},
|
|
85
|
+
// m5k:workflows:end
|
|
64
86
|
resend: resendApiKey ? { apiKey: resendApiKey } : undefined,
|
|
65
87
|
email: {
|
|
66
88
|
mode: resendApiKey ? "send" : "store",
|
|
@@ -93,6 +115,15 @@ export const builtBackendApp = createBackendApp(
|
|
|
93
115
|
[
|
|
94
116
|
new EmailModule(templates as never),
|
|
95
117
|
new AuthModule(),
|
|
118
|
+
// m5k:ai:start
|
|
119
|
+
new AIModule({
|
|
120
|
+
libs: { mastra, openrouter },
|
|
121
|
+
}),
|
|
122
|
+
// m5k:ai:end
|
|
123
|
+
// m5k:files:start
|
|
124
|
+
new FileModule(),
|
|
125
|
+
// m5k:files:end
|
|
126
|
+
// m5k:workflows:start
|
|
96
127
|
new WorkflowModule({
|
|
97
128
|
queues: {
|
|
98
129
|
fast: { concurrency: 5 },
|
|
@@ -104,6 +135,11 @@ export const builtBackendApp = createBackendApp(
|
|
|
104
135
|
},
|
|
105
136
|
reconcile: { enabled: true },
|
|
106
137
|
}),
|
|
138
|
+
new DemoWorkflowModule(),
|
|
139
|
+
// m5k:workflows:end
|
|
140
|
+
// m5k:notifications:start
|
|
141
|
+
new NotificationModule(),
|
|
142
|
+
// m5k:notifications:end
|
|
107
143
|
new PostsModule(),
|
|
108
144
|
new EmailPreviewModule({ allowDelete: process.env.NODE_ENV !== "production" }),
|
|
109
145
|
// m5k:test-harness:start
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Mastra } from "@mastra/core";
|
|
2
|
+
import { Agent } from "@mastra/core/agent";
|
|
3
|
+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
4
|
+
import { STARTER_ASSISTANT_AGENT_ID } from "{{PACKAGE_SCOPE}}/shared/modules/conversation/conversation.constants";
|
|
5
|
+
|
|
6
|
+
/** Cheap OpenRouter chat model for the starter Conversation example. */
|
|
7
|
+
const STARTER_CHAT_MODEL = "google/gemini-2.5-flash";
|
|
8
|
+
|
|
9
|
+
export const openrouter = createOpenRouter({
|
|
10
|
+
apiKey: process.env.OPENROUTER_API_KEY ?? "",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const assistant = new Agent({
|
|
14
|
+
id: STARTER_ASSISTANT_AGENT_ID,
|
|
15
|
+
name: "Assistant",
|
|
16
|
+
instructions: "You are a helpful assistant for this starter app. Keep answers concise.",
|
|
17
|
+
model: openrouter.chat(STARTER_CHAT_MODEL, {
|
|
18
|
+
usage: { include: true },
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const mastra = new Mastra({
|
|
23
|
+
agents: {
|
|
24
|
+
[STARTER_ASSISTANT_AGENT_ID]: assistant,
|
|
25
|
+
},
|
|
26
|
+
});
|
package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.module.ts.tpl
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createBackendRouterMap } from "@m5kdev/backend/app";
|
|
2
|
+
import type { AuthModule } from "@m5kdev/backend/modules/auth/auth.module";
|
|
3
|
+
import {
|
|
4
|
+
BaseModule,
|
|
5
|
+
type ModuleServicesContext,
|
|
6
|
+
type ModuleTRPCContext,
|
|
7
|
+
type TableMap,
|
|
8
|
+
} from "@m5kdev/backend/modules/base/base.module";
|
|
9
|
+
import type { WorkflowModule } from "@m5kdev/backend/modules/workflow/workflow.module";
|
|
10
|
+
import { DemoWorkflowService } from "./demo-workflow.service";
|
|
11
|
+
import { createDemoWorkflowTRPC } from "./demo-workflow.trpc";
|
|
12
|
+
|
|
13
|
+
type DemoWorkflowModuleDeps = { workflow: WorkflowModule; auth: AuthModule };
|
|
14
|
+
type DemoWorkflowModuleServices = {
|
|
15
|
+
demoWorkflow: DemoWorkflowService;
|
|
16
|
+
};
|
|
17
|
+
type DemoWorkflowModuleRouters = {
|
|
18
|
+
demoWorkflow: ReturnType<typeof createDemoWorkflowTRPC>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class DemoWorkflowModule extends BaseModule<
|
|
22
|
+
DemoWorkflowModuleDeps,
|
|
23
|
+
TableMap,
|
|
24
|
+
Record<string, never>,
|
|
25
|
+
DemoWorkflowModuleServices,
|
|
26
|
+
DemoWorkflowModuleRouters
|
|
27
|
+
> {
|
|
28
|
+
readonly id = "demo-workflow";
|
|
29
|
+
override readonly dependsOn = ["workflow", "auth"] as const;
|
|
30
|
+
|
|
31
|
+
override services({
|
|
32
|
+
deps,
|
|
33
|
+
}: ModuleServicesContext<DemoWorkflowModuleDeps, Record<string, never>>) {
|
|
34
|
+
return {
|
|
35
|
+
demoWorkflow: new DemoWorkflowService({
|
|
36
|
+
workflow: deps.workflow.services.workflow,
|
|
37
|
+
auth: deps.auth.services.auth,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override trpc({
|
|
43
|
+
trpc,
|
|
44
|
+
services,
|
|
45
|
+
}: ModuleTRPCContext<DemoWorkflowModuleDeps, DemoWorkflowModuleServices>) {
|
|
46
|
+
return createBackendRouterMap(
|
|
47
|
+
"demoWorkflow",
|
|
48
|
+
createDemoWorkflowTRPC(trpc, services.demoWorkflow)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
package/templates/minimal-app/apps/server/src/modules/demo-workflow/demo-workflow.service.ts.tpl
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AuthService } from "@m5kdev/backend/modules/auth/auth.service";
|
|
2
|
+
import type { ServerResultAsync } from "@m5kdev/backend/modules/base/base.dto";
|
|
3
|
+
import { BaseService } from "@m5kdev/backend/modules/base/base.service";
|
|
4
|
+
import type { WorkflowService } from "@m5kdev/backend/modules/workflow/workflow.service";
|
|
5
|
+
import type { FireAndForgetJobDefinition } from "@m5kdev/backend/modules/workflow/workflow.types";
|
|
6
|
+
import type { Context } from "@m5kdev/backend/utils/trpc";
|
|
7
|
+
import { workflowTriggerOutputSchema } from "@m5kdev/commons/modules/workflow/workflow.schema";
|
|
8
|
+
import { DEMO_WORKFLOW_SERVER_EVENT_RESOURCE } from "{{PACKAGE_SCOPE}}/shared/modules/demo-workflow/demo-workflow.constants";
|
|
9
|
+
import { ok } from "neverthrow";
|
|
10
|
+
|
|
11
|
+
const DEMO_PING_JOB_NAME = "demo.ping";
|
|
12
|
+
|
|
13
|
+
interface DemoPingJobPayload {
|
|
14
|
+
readonly userId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class DemoWorkflowService extends BaseService<
|
|
18
|
+
Record<string, never>,
|
|
19
|
+
{ workflow: WorkflowService; auth: AuthService },
|
|
20
|
+
Context
|
|
21
|
+
> {
|
|
22
|
+
readonly demoPingJob: FireAndForgetJobDefinition<DemoPingJobPayload>;
|
|
23
|
+
|
|
24
|
+
constructor(services: { workflow: WorkflowService; auth: AuthService }) {
|
|
25
|
+
super({}, services);
|
|
26
|
+
|
|
27
|
+
this.demoPingJob = this.service.workflow
|
|
28
|
+
.job<DemoPingJobPayload>({
|
|
29
|
+
name: DEMO_PING_JOB_NAME,
|
|
30
|
+
queue: "fast",
|
|
31
|
+
meta: (payload) => ({ userId: payload.userId }),
|
|
32
|
+
})
|
|
33
|
+
.handle(async (payload) => {
|
|
34
|
+
this.service.auth.userEmit({
|
|
35
|
+
userId: payload.userId,
|
|
36
|
+
resource: DEMO_WORKFLOW_SERVER_EVENT_RESOURCE,
|
|
37
|
+
id: DEMO_PING_JOB_NAME,
|
|
38
|
+
change: "updated",
|
|
39
|
+
organizationId: null,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
readonly runDemo = this.procedure("runDemo")
|
|
45
|
+
.output(workflowTriggerOutputSchema)
|
|
46
|
+
.requireAuth()
|
|
47
|
+
.handle(async ({ ctx }): ServerResultAsync<{ jobId: string }> => {
|
|
48
|
+
const jobId = await this.demoPingJob.trigger({ userId: ctx.actor.userId });
|
|
49
|
+
return ok({ jobId });
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { handleTRPCResult, type TRPCMethods } from "@m5kdev/backend/utils/trpc";
|
|
2
|
+
import { workflowTriggerOutputSchema } from "@m5kdev/commons/modules/workflow/workflow.schema";
|
|
3
|
+
import type { DemoWorkflowService } from "./demo-workflow.service";
|
|
4
|
+
|
|
5
|
+
export function createDemoWorkflowTRPC(
|
|
6
|
+
{ router, privateProcedure }: TRPCMethods,
|
|
7
|
+
demoWorkflowService: DemoWorkflowService
|
|
8
|
+
) {
|
|
9
|
+
return router({
|
|
10
|
+
run: privateProcedure
|
|
11
|
+
.output(workflowTriggerOutputSchema)
|
|
12
|
+
.mutation(async ({ ctx }) =>
|
|
13
|
+
handleTRPCResult(await demoWorkflowService.runDemo(undefined, ctx))
|
|
14
|
+
),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { members, organizations, teams, users } from "@m5kdev/backend/modules/auth/auth.db";
|
|
2
|
-
import { integer, sqliteTable as table, text } from "drizzle-orm/sqlite-core";
|
|
3
|
-
import { v4 as uuidv4 } from "uuid";
|
|
4
|
-
|
|
5
|
-
export const posts = table("posts", {
|
|
6
|
-
id: text("id").primaryKey().$default(uuidv4),
|
|
7
|
-
authorUserId: text("author_user_id").references(() => users.id, {
|
|
8
|
-
onDelete: "set null",
|
|
9
|
-
}),
|
|
10
|
-
memberId: text("member_id").references(() => members.id, {
|
|
11
|
-
onDelete: "set null",
|
|
12
|
-
}),
|
|
13
|
-
organizationId: text("organization_id").references(() => organizations.id, {
|
|
14
|
-
onDelete: "set null",
|
|
15
|
-
}),
|
|
16
|
-
teamId: text("team_id").references(() => teams.id, { onDelete: "set null" }),
|
|
17
|
-
title: text("title").notNull(),
|
|
18
|
-
slug: text("slug").notNull().unique(),
|
|
19
|
-
excerpt: text("excerpt"),
|
|
20
|
-
content: text("content").notNull(),
|
|
21
|
-
status: text("status").$type<"draft" | "published">().notNull().default("draft"),
|
|
22
|
-
publishedAt: integer("published_at", { mode: "timestamp" }),
|
|
23
|
-
createdAt: integer("created_at", { mode: "timestamp" })
|
|
24
|
-
.notNull()
|
|
25
|
-
.$default(() => new Date()),
|
|
26
|
-
updatedAt: integer("updated_at", { mode: "timestamp" }),
|
|
27
|
-
deletedAt: integer("deleted_at", { mode: "timestamp" }),
|
|
28
|
-
});
|
|
1
|
+
import { members, organizations, teams, users } from "@m5kdev/backend/modules/auth/auth.db";
|
|
2
|
+
import { integer, sqliteTable as table, text } from "drizzle-orm/sqlite-core";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
|
|
5
|
+
export const posts = table("posts", {
|
|
6
|
+
id: text("id").primaryKey().$default(uuidv4),
|
|
7
|
+
authorUserId: text("author_user_id").references(() => users.id, {
|
|
8
|
+
onDelete: "set null",
|
|
9
|
+
}),
|
|
10
|
+
memberId: text("member_id").references(() => members.id, {
|
|
11
|
+
onDelete: "set null",
|
|
12
|
+
}),
|
|
13
|
+
organizationId: text("organization_id").references(() => organizations.id, {
|
|
14
|
+
onDelete: "set null",
|
|
15
|
+
}),
|
|
16
|
+
teamId: text("team_id").references(() => teams.id, { onDelete: "set null" }),
|
|
17
|
+
title: text("title").notNull(),
|
|
18
|
+
slug: text("slug").notNull().unique(),
|
|
19
|
+
excerpt: text("excerpt"),
|
|
20
|
+
content: text("content").notNull(),
|
|
21
|
+
status: text("status").$type<"draft" | "published">().notNull().default("draft"),
|
|
22
|
+
publishedAt: integer("published_at", { mode: "timestamp" }),
|
|
23
|
+
createdAt: integer("created_at", { mode: "timestamp" })
|
|
24
|
+
.notNull()
|
|
25
|
+
.$default(() => new Date()),
|
|
26
|
+
updatedAt: integer("updated_at", { mode: "timestamp" }),
|
|
27
|
+
deletedAt: integer("deleted_at", { mode: "timestamp" }),
|
|
28
|
+
});
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { flattenNestedGrants } from "@m5kdev/backend/modules/base/base.grants";
|
|
2
|
-
|
|
3
|
-
export const postsGrants = flattenNestedGrants({
|
|
4
|
-
cash: {
|
|
5
|
-
user: {
|
|
6
|
-
admin: {
|
|
7
|
-
read: "all",
|
|
8
|
-
write: "all",
|
|
9
|
-
delete: "all",
|
|
10
|
-
publish: "all",
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
organization: {
|
|
14
|
-
owner: {
|
|
15
|
-
read: "org",
|
|
16
|
-
write: "org",
|
|
17
|
-
delete: "org",
|
|
18
|
-
publish: "org",
|
|
19
|
-
},
|
|
20
|
-
member: {
|
|
21
|
-
read: "own",
|
|
22
|
-
write: "own",
|
|
23
|
-
delete: "own",
|
|
24
|
-
publish: "own",
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
});
|
|
1
|
+
import { flattenNestedGrants } from "@m5kdev/backend/modules/base/base.grants";
|
|
2
|
+
|
|
3
|
+
export const postsGrants = flattenNestedGrants({
|
|
4
|
+
cash: {
|
|
5
|
+
user: {
|
|
6
|
+
admin: {
|
|
7
|
+
read: "all",
|
|
8
|
+
write: "all",
|
|
9
|
+
delete: "all",
|
|
10
|
+
publish: "all",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
organization: {
|
|
14
|
+
owner: {
|
|
15
|
+
read: "org",
|
|
16
|
+
write: "org",
|
|
17
|
+
delete: "org",
|
|
18
|
+
publish: "org",
|
|
19
|
+
},
|
|
20
|
+
member: {
|
|
21
|
+
read: "own",
|
|
22
|
+
write: "own",
|
|
23
|
+
delete: "own",
|
|
24
|
+
publish: "own",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -1,54 +1,57 @@
|
|
|
1
|
-
import { createBackendRouterMap } from "@m5kdev/backend/app";
|
|
2
|
-
import {
|
|
3
|
-
BaseModule,
|
|
4
|
-
type ModuleRepositoriesContext,
|
|
5
|
-
type ModuleServicesContext,
|
|
6
|
-
type ModuleTRPCContext,
|
|
7
|
-
} from "@m5kdev/backend/modules/base/base.module";
|
|
8
|
-
import type * as postsTables from "./posts.db";
|
|
9
|
-
import { postsGrants } from "./posts.grants";
|
|
10
|
-
import { PostsRepository } from "./posts.repository";
|
|
11
|
-
import { PostsService } from "./posts.service";
|
|
12
|
-
import { createPostsTRPC } from "./posts.trpc";
|
|
13
|
-
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
1
|
+
import { createBackendRouterMap } from "@m5kdev/backend/app";
|
|
2
|
+
import {
|
|
3
|
+
BaseModule,
|
|
4
|
+
type ModuleRepositoriesContext,
|
|
5
|
+
type ModuleServicesContext,
|
|
6
|
+
type ModuleTRPCContext,
|
|
7
|
+
} from "@m5kdev/backend/modules/base/base.module";
|
|
8
|
+
import type * as postsTables from "./posts.db";
|
|
9
|
+
import { postsGrants } from "./posts.grants";
|
|
10
|
+
import { PostsRepository } from "./posts.repository";
|
|
11
|
+
import { PostsService } from "./posts.service";
|
|
12
|
+
import { createPostsTRPC } from "./posts.trpc";
|
|
13
|
+
|
|
14
|
+
type PostsModuleDeps = Record<string, never>;
|
|
15
|
+
type PostsModuleTables = typeof postsTables;
|
|
16
|
+
type PostsModuleRepositories = {
|
|
17
|
+
posts: PostsRepository;
|
|
18
|
+
};
|
|
19
|
+
type PostsModuleServices = {
|
|
20
|
+
posts: PostsService;
|
|
21
|
+
};
|
|
22
|
+
type PostsModuleRouters = {
|
|
23
|
+
posts: ReturnType<typeof createPostsTRPC>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export class PostsModule extends BaseModule<
|
|
27
|
+
PostsModuleDeps,
|
|
28
|
+
PostsModuleTables,
|
|
29
|
+
PostsModuleRepositories,
|
|
30
|
+
PostsModuleServices,
|
|
31
|
+
PostsModuleRouters
|
|
32
|
+
> {
|
|
33
|
+
readonly id = "posts";
|
|
34
|
+
override readonly dbDependsOn = ["auth"] as const;
|
|
35
|
+
|
|
36
|
+
override repositories({ db }: ModuleRepositoriesContext<PostsModuleDeps, PostsModuleTables>) {
|
|
37
|
+
return {
|
|
38
|
+
posts: new PostsRepository({
|
|
39
|
+
orm: db.orm,
|
|
40
|
+
schema: db.schema,
|
|
41
|
+
table: db.schema.posts,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override services({
|
|
47
|
+
repositories,
|
|
48
|
+
}: ModuleServicesContext<PostsModuleDeps, PostsModuleRepositories>) {
|
|
49
|
+
return {
|
|
50
|
+
posts: new PostsService({ posts: repositories.posts }, {}, postsGrants),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override trpc({ trpc, services }: ModuleTRPCContext<PostsModuleDeps, PostsModuleServices>) {
|
|
55
|
+
return createBackendRouterMap("posts", createPostsTRPC(trpc, services.posts));
|
|
56
|
+
}
|
|
57
|
+
}
|