create-forgeon 0.3.15 → 0.3.16
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 +4 -2
- package/src/core/docs.test.mjs +79 -40
- package/src/core/scaffold.test.mjs +99 -0
- package/src/modules/db-prisma.mjs +23 -55
- package/src/modules/executor.test.mjs +132 -36
- package/src/modules/files-access.mjs +27 -98
- package/src/modules/files-image.mjs +26 -100
- package/src/modules/files-quotas.mjs +67 -87
- package/src/modules/files.mjs +35 -104
- package/src/modules/i18n.mjs +17 -121
- package/src/modules/idempotency.test.mjs +174 -0
- package/src/modules/jwt-auth.mjs +90 -209
- package/src/modules/logger.mjs +0 -9
- package/src/modules/probes.test.mjs +202 -0
- package/src/modules/queue.mjs +325 -443
- package/src/modules/rate-limit.mjs +22 -66
- package/src/modules/rbac.mjs +27 -67
- package/src/modules/scheduler.mjs +44 -167
- package/src/modules/shared/nest-runtime-wiring.mjs +110 -0
- package/src/modules/shared/probes.mjs +235 -0
- package/src/modules/sync-integrations.mjs +54 -21
- package/src/modules/sync-integrations.test.mjs +220 -0
- package/src/run-add-module.test.mjs +153 -0
- package/templates/base/README.md +7 -55
- package/templates/base/apps/web/src/App.tsx +70 -42
- package/templates/base/apps/web/src/probes.ts +61 -0
- package/templates/base/apps/web/src/styles.css +86 -25
- package/templates/base/package.json +21 -15
- package/templates/base/scripts/forgeon-sync-integrations.mjs +55 -11
- package/templates/module-presets/files-quotas/packages/files-quotas/src/forgeon-files-quotas.module.ts +12 -4
- package/templates/module-presets/i18n/apps/web/src/App.tsx +68 -41
- package/templates/module-presets/logger/packages/logger/src/index.ts +0 -1
- package/templates/base/docs/AI/ARCHITECTURE.md +0 -85
- package/templates/base/docs/AI/MODULE_CHECKS.md +0 -28
- package/templates/base/docs/AI/MODULE_SPEC.md +0 -77
- package/templates/base/docs/AI/PROJECT.md +0 -43
- package/templates/base/docs/AI/ROADMAP.md +0 -171
- package/templates/base/docs/AI/TASKS.md +0 -60
- package/templates/base/docs/AI/VALIDATION.md +0 -31
- package/templates/base/docs/README.md +0 -18
- package/templates/module-presets/logger/packages/logger/src/http-logging.interceptor.ts +0 -94
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { Module } from '@nestjs/common';
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
2
|
import { ForgeonFilesModule } from '@forgeon/files';
|
|
3
3
|
import { FilesQuotasConfigModule } from './files-quotas-config.module';
|
|
4
4
|
import { FilesQuotasService } from './files-quotas.service';
|
|
5
5
|
|
|
6
|
+
const FORGEON_FILES_UPLOAD_QUOTA_SERVICE = 'FORGEON_FILES_UPLOAD_QUOTA_SERVICE';
|
|
7
|
+
|
|
6
8
|
@Module({
|
|
7
9
|
imports: [ForgeonFilesModule, FilesQuotasConfigModule],
|
|
8
|
-
providers: [
|
|
9
|
-
|
|
10
|
+
providers: [
|
|
11
|
+
FilesQuotasService,
|
|
12
|
+
{
|
|
13
|
+
provide: FORGEON_FILES_UPLOAD_QUOTA_SERVICE,
|
|
14
|
+
useExisting: FilesQuotasService,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
exports: [FilesQuotasConfigModule, FilesQuotasService, FORGEON_FILES_UPLOAD_QUOTA_SERVICE],
|
|
10
18
|
})
|
|
11
|
-
export class ForgeonFilesQuotasModule {}
|
|
19
|
+
export class ForgeonFilesQuotasModule {}
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import i18n from './i18n';
|
|
4
4
|
import * as i18nWeb from '@forgeon/i18n-web';
|
|
5
5
|
import type { I18nLocale } from '@forgeon/i18n-web';
|
|
6
|
+
import { probeDefinitions, type ProbeDefinition, type ProbeResult } from './probes';
|
|
6
7
|
import './styles.css';
|
|
7
8
|
|
|
8
|
-
type
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
type ProbeState = {
|
|
10
|
+
result: ProbeResult | null;
|
|
11
|
+
error: string | null;
|
|
12
|
+
loading: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const emptyProbeState: ProbeState = {
|
|
16
|
+
result: null,
|
|
17
|
+
error: null,
|
|
18
|
+
loading: false,
|
|
11
19
|
};
|
|
12
20
|
|
|
13
21
|
export default function App() {
|
|
14
22
|
const { t } = useTranslation(['ui']);
|
|
15
23
|
const { I18N_LOCALES, getInitialLocale, persistLocale, toLangQuery } = i18nWeb;
|
|
16
24
|
const [locale, setLocale] = useState<I18nLocale>(getInitialLocale);
|
|
17
|
-
const [
|
|
18
|
-
const [errorProbeResult, setErrorProbeResult] = useState<ProbeResult | null>(null);
|
|
19
|
-
const [validationProbeResult, setValidationProbeResult] = useState<ProbeResult | null>(null);
|
|
20
|
-
const [networkError, setNetworkError] = useState<string | null>(null);
|
|
25
|
+
const [probeState, setProbeState] = useState<Record<string, ProbeState>>({});
|
|
21
26
|
|
|
22
27
|
const changeLocale = (nextLocale: I18nLocale) => {
|
|
23
28
|
setLocale(nextLocale);
|
|
@@ -25,12 +30,12 @@ export default function App() {
|
|
|
25
30
|
void i18n.changeLanguage(nextLocale);
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
const requestProbe = async (
|
|
29
|
-
const response = await fetch(`/api${path}${toLangQuery(locale)}`, {
|
|
30
|
-
...(
|
|
33
|
+
const requestProbe = async (probe: ProbeDefinition): Promise<ProbeResult> => {
|
|
34
|
+
const response = await fetch(`/api${probe.path}${toLangQuery(locale)}`, {
|
|
35
|
+
...(probe.request ?? {}),
|
|
31
36
|
cache: 'no-store',
|
|
32
37
|
headers: {
|
|
33
|
-
...(
|
|
38
|
+
...(probe.request?.headers ?? {}),
|
|
34
39
|
'Accept-Language': locale,
|
|
35
40
|
},
|
|
36
41
|
});
|
|
@@ -48,27 +53,38 @@ export default function App() {
|
|
|
48
53
|
};
|
|
49
54
|
};
|
|
50
55
|
|
|
51
|
-
const runProbe = async (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const runProbe = async (probe: ProbeDefinition) => {
|
|
57
|
+
setProbeState((current) => ({
|
|
58
|
+
...current,
|
|
59
|
+
[probe.id]: {
|
|
60
|
+
...(current[probe.id] ?? emptyProbeState),
|
|
61
|
+
error: null,
|
|
62
|
+
loading: true,
|
|
63
|
+
},
|
|
64
|
+
}));
|
|
65
|
+
|
|
57
66
|
try {
|
|
58
|
-
const result = await requestProbe(
|
|
59
|
-
|
|
67
|
+
const result = await requestProbe(probe);
|
|
68
|
+
setProbeState((current) => ({
|
|
69
|
+
...current,
|
|
70
|
+
[probe.id]: {
|
|
71
|
+
result,
|
|
72
|
+
error: null,
|
|
73
|
+
loading: false,
|
|
74
|
+
},
|
|
75
|
+
}));
|
|
60
76
|
} catch (err) {
|
|
61
|
-
|
|
77
|
+
setProbeState((current) => ({
|
|
78
|
+
...current,
|
|
79
|
+
[probe.id]: {
|
|
80
|
+
result: current[probe.id]?.result ?? null,
|
|
81
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
82
|
+
loading: false,
|
|
83
|
+
},
|
|
84
|
+
}));
|
|
62
85
|
}
|
|
63
86
|
};
|
|
64
87
|
|
|
65
|
-
const renderResult = (title: string, result: ProbeResult | null) => (
|
|
66
|
-
<section>
|
|
67
|
-
<h3>{title}</h3>
|
|
68
|
-
{result ? <pre>{JSON.stringify(result, null, 2)}</pre> : null}
|
|
69
|
-
</section>
|
|
70
|
-
);
|
|
71
|
-
|
|
72
88
|
return (
|
|
73
89
|
<main className="page">
|
|
74
90
|
<h1>Forgeon Fullstack Scaffold</h1>
|
|
@@ -85,19 +101,30 @@ export default function App() {
|
|
|
85
101
|
</option>
|
|
86
102
|
))}
|
|
87
103
|
</select>
|
|
88
|
-
<div className="
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
<div id="probes" className="probes">
|
|
105
|
+
{probeDefinitions.map((probe) => {
|
|
106
|
+
const current = probeState[probe.id] ?? emptyProbeState;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<section key={probe.id} className="probe">
|
|
110
|
+
<div className="probe-header">
|
|
111
|
+
<h2>{probe.title}</h2>
|
|
112
|
+
<button type="button" onClick={() => runProbe(probe)} disabled={current.loading}>
|
|
113
|
+
{current.loading ? 'Running...' : probe.buttonLabel}
|
|
114
|
+
</button>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="probe-output">
|
|
117
|
+
<h3>{probe.resultTitle}</h3>
|
|
118
|
+
{current.error ? <p className="error">{current.error}</p> : null}
|
|
119
|
+
{current.result ? <pre>{JSON.stringify(current.result, null, 2)}</pre> : null}
|
|
120
|
+
{!current.error && !current.result ? (
|
|
121
|
+
<p className="placeholder">No probe result yet.</p>
|
|
122
|
+
) : null}
|
|
123
|
+
</div>
|
|
124
|
+
</section>
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
96
127
|
</div>
|
|
97
|
-
{renderResult('Health response', healthResult)}
|
|
98
|
-
{renderResult('Error probe response', errorProbeResult)}
|
|
99
|
-
{renderResult('Validation probe response', validationProbeResult)}
|
|
100
|
-
{networkError ? <p className="error">{networkError}</p> : null}
|
|
101
128
|
</main>
|
|
102
129
|
);
|
|
103
|
-
}
|
|
130
|
+
}
|
|
@@ -4,6 +4,5 @@ export * from './logger-config.service';
|
|
|
4
4
|
export * from './logger-config.module';
|
|
5
5
|
export * from './forgeon-logger.service';
|
|
6
6
|
export * from './http-logging.middleware';
|
|
7
|
-
export * from './http-logging.interceptor';
|
|
8
7
|
export * from './request-id.middleware';
|
|
9
8
|
export * from './forgeon-logger.module';
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# ARCHITECTURE
|
|
2
|
-
|
|
3
|
-
## Monorepo Layout
|
|
4
|
-
|
|
5
|
-
- `apps/*` - deployable apps
|
|
6
|
-
- `packages/*` - reusable modules/presets
|
|
7
|
-
- `infra/*` - runtime infrastructure
|
|
8
|
-
- `resources/*` - static assets (translations)
|
|
9
|
-
|
|
10
|
-
Canonical stack is fixed in this stage:
|
|
11
|
-
- NestJS + React + Prisma/Postgres + Docker
|
|
12
|
-
- Proxy preset can be `caddy`, `nginx`, or `none`
|
|
13
|
-
|
|
14
|
-
## Environment Flags
|
|
15
|
-
|
|
16
|
-
- `PORT` - API port (default 3000)
|
|
17
|
-
- `API_PREFIX` - global API prefix (default `api`)
|
|
18
|
-
- `DATABASE_URL` - Prisma Postgres connection
|
|
19
|
-
- `I18N_DEFAULT_LANG` - default language
|
|
20
|
-
- `I18N_FALLBACK_LANG` - fallback language
|
|
21
|
-
|
|
22
|
-
## Config Strategy
|
|
23
|
-
|
|
24
|
-
- `@forgeon/core` owns base runtime config, global error envelope/filter, and validation pipe defaults.
|
|
25
|
-
- Core config is validated with Zod and exposed through typed accessors.
|
|
26
|
-
- Add-modules own and validate only their module-specific env keys.
|
|
27
|
-
- i18n is an add-module; when installed, it uses its own env keys.
|
|
28
|
-
|
|
29
|
-
## Default DB Stack
|
|
30
|
-
|
|
31
|
-
Current default is Prisma + Postgres.
|
|
32
|
-
|
|
33
|
-
- Prisma schema and migrations live in `apps/api/prisma`
|
|
34
|
-
- DB access is encapsulated via `DbPrismaModule` in `@forgeon/db-prisma`
|
|
35
|
-
- `db-prisma` is treated as default-applied behavior in scaffold generation.
|
|
36
|
-
- Future direction: this default DB layer may be extracted to an explicit add-module/preset and optionally controlled by a CLI flag.
|
|
37
|
-
- Additional DB presets are out of scope for the current milestone.
|
|
38
|
-
|
|
39
|
-
## Module Strategy
|
|
40
|
-
|
|
41
|
-
Reusable features should be added as fullstack add-modules:
|
|
42
|
-
|
|
43
|
-
- `contracts` package (shared DTO/routes/errors)
|
|
44
|
-
- `api` package (NestJS integration)
|
|
45
|
-
- `web` package (React integration)
|
|
46
|
-
|
|
47
|
-
Reference: `docs/AI/MODULE_SPEC.md`.
|
|
48
|
-
|
|
49
|
-
## Integration Sync Strategy
|
|
50
|
-
|
|
51
|
-
- Integration orchestration is a default project toolchain command:
|
|
52
|
-
- `pnpm forgeon:sync-integrations`
|
|
53
|
-
- Purpose:
|
|
54
|
-
- keep add-modules composable when installed in arbitrary order;
|
|
55
|
-
- apply module-to-module integration patches idempotently.
|
|
56
|
-
- Rule:
|
|
57
|
-
- each add-module patches only itself;
|
|
58
|
-
- cross-module changes are allowed only in integration sync rules.
|
|
59
|
-
- Current integration:
|
|
60
|
-
- `jwt-auth + db-adapter` (current provider: `db-prisma`; persistent refresh-token store wiring + schema/migration sync).
|
|
61
|
-
- Pair sync is explicit (opt-in), not automatic after `add`.
|
|
62
|
-
- Run `pnpm forgeon:sync-integrations` when you want to apply module-pair integrations.
|
|
63
|
-
- Swagger auth decorators are intentionally not auto-patched.
|
|
64
|
-
- Future option: this may return as an explicit optional command (not default automatic behavior).
|
|
65
|
-
|
|
66
|
-
## TypeScript Module Format Policy
|
|
67
|
-
|
|
68
|
-
- `apps/api`, `packages/core`, and backend runtime packages use Node-oriented config:
|
|
69
|
-
- `tsconfig.base.node.json`
|
|
70
|
-
- Frontend-consumed shared packages (especially contracts/web helpers) use ESM config:
|
|
71
|
-
- `tsconfig.base.esm.json`
|
|
72
|
-
- Contracts packages are ESM-first and imported via package entrypoints only.
|
|
73
|
-
- Cross-package imports from `/src/*` are disallowed.
|
|
74
|
-
|
|
75
|
-
## Error Handling Strategy
|
|
76
|
-
|
|
77
|
-
- `@forgeon/core` owns the global HTTP error envelope and filter.
|
|
78
|
-
- API apps import `CoreErrorsModule` and register `CoreExceptionFilter` globally.
|
|
79
|
-
- Envelope fields:
|
|
80
|
-
- `error.code`
|
|
81
|
-
- `error.message`
|
|
82
|
-
- `error.status`
|
|
83
|
-
- `error.details` (optional)
|
|
84
|
-
- `error.requestId` (optional)
|
|
85
|
-
- `error.timestamp`
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# MODULE CHECKS
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
Define mandatory runtime verification hooks for Forgeon modules.
|
|
6
|
-
|
|
7
|
-
If a module can be validated through a safe API call, it must provide:
|
|
8
|
-
|
|
9
|
-
1. A probe endpoint in API (`/api/health/*`).
|
|
10
|
-
2. A probe trigger on default web page (`apps/web/src/App.tsx`).
|
|
11
|
-
3. A visible result block in UI with HTTP status and JSON body.
|
|
12
|
-
|
|
13
|
-
## Current Baseline Probes
|
|
14
|
-
|
|
15
|
-
- `core-errors`: `GET /api/health/error` (returns error envelope, expected `409`)
|
|
16
|
-
- `core-validation`: `GET /api/health/validation` without `value` (expected `400`)
|
|
17
|
-
- `db-prisma` (when installed): `POST /api/health/db` (creates probe user and returns it, expected `201`)
|
|
18
|
-
|
|
19
|
-
## Rules For Future Modules
|
|
20
|
-
|
|
21
|
-
- Probe path should be explicit and feature-scoped (`/api/health/<feature>`).
|
|
22
|
-
- Probe must be deterministic and documented (expected status + payload shape).
|
|
23
|
-
- If probe writes data, it must use clearly marked probe/test records.
|
|
24
|
-
- Probe should not require hidden setup beyond documented env/dependencies.
|
|
25
|
-
- `create-forgeon add <module>` must wire both API probe and web probe UI when feasible.
|
|
26
|
-
- Web probes should be appended to the existing probe UI structure in `apps/web/src/App.tsx`:
|
|
27
|
-
- add new action button at the end of `<div className="actions">`
|
|
28
|
-
- add new result block before the `networkError` render block
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# MODULE SPEC
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Define one repeatable fullstack pattern for Forgeon add-modules.
|
|
6
|
-
|
|
7
|
-
Most feature modules should be split into:
|
|
8
|
-
|
|
9
|
-
1. `@forgeon/<feature>-contracts`
|
|
10
|
-
2. `@forgeon/<feature>-api`
|
|
11
|
-
3. `@forgeon/<feature>-web`
|
|
12
|
-
|
|
13
|
-
Exception:
|
|
14
|
-
|
|
15
|
-
- backend-only infrastructure or security modules may use a single runtime package (`@forgeon/<feature>`) when shared contracts and a dedicated web package add no real value.
|
|
16
|
-
|
|
17
|
-
## 1) Contracts Package
|
|
18
|
-
|
|
19
|
-
Single source of truth shared by backend and frontend.
|
|
20
|
-
|
|
21
|
-
Must contain:
|
|
22
|
-
|
|
23
|
-
- DTO/request/response types
|
|
24
|
-
- route constants (`API.<feature>.*`)
|
|
25
|
-
- error codes (`<FEATURE>_*`)
|
|
26
|
-
- shared constants (header/cookie names)
|
|
27
|
-
- package entrypoint exports only (`@forgeon/<feature>-contracts`)
|
|
28
|
-
|
|
29
|
-
Should contain:
|
|
30
|
-
|
|
31
|
-
- zod schemas + inferred TS types
|
|
32
|
-
|
|
33
|
-
Build/runtime rules:
|
|
34
|
-
|
|
35
|
-
- ESM-first package (`"type": "module"`, `module: "ESNext"`)
|
|
36
|
-
- extends `tsconfig.base.esm.json`
|
|
37
|
-
- no NestJS or browser-only runtime dependencies
|
|
38
|
-
- no imports from sibling package `/src/*` paths
|
|
39
|
-
|
|
40
|
-
## 2) API Package
|
|
41
|
-
|
|
42
|
-
NestJS module integrating contracts into backend runtime.
|
|
43
|
-
|
|
44
|
-
Must contain:
|
|
45
|
-
|
|
46
|
-
- module/service/controller
|
|
47
|
-
- guards/strategies (if auth/security related)
|
|
48
|
-
- config keys
|
|
49
|
-
- minimal e2e test path
|
|
50
|
-
- integration with `@forgeon/core` errors/logging
|
|
51
|
-
|
|
52
|
-
## 3) Web Package
|
|
53
|
-
|
|
54
|
-
React integration layer for the same feature.
|
|
55
|
-
|
|
56
|
-
Must contain:
|
|
57
|
-
|
|
58
|
-
- provider/hooks/store
|
|
59
|
-
- route guard (if feature requires auth/access)
|
|
60
|
-
- API client helpers using contracts route constants/types
|
|
61
|
-
- token/header/cookie wiring where relevant
|
|
62
|
-
|
|
63
|
-
## Acceptance Criteria
|
|
64
|
-
|
|
65
|
-
- No duplicate route strings across api/web.
|
|
66
|
-
- No duplicate error-code enums across api/web.
|
|
67
|
-
- Contracts package can be imported from both sides without circular dependencies.
|
|
68
|
-
- Contracts package exports are stable from `dist/index` entrypoint.
|
|
69
|
-
- Module has docs under `docs/AI/MODULES/<module-id>.md`.
|
|
70
|
-
- Module docs must explain: why it exists, what it adds, how it works, how to use it, how to configure it, and current operational limits.
|
|
71
|
-
- If module behavior can be runtime-checked, it also includes API+Web probe hooks (see `docs/AI/MODULE_CHECKS.md`).
|
|
72
|
-
- If i18n is enabled, module-specific namespaces must be created and wired for both API and web.
|
|
73
|
-
- If module is added before i18n, namespace templates must still be prepared and applied when i18n is installed later.
|
|
74
|
-
- Module integration with other modules must be represented as idempotent sync rules and runnable via `pnpm forgeon:sync-integrations`.
|
|
75
|
-
- `create-forgeon add <module-id>` does not auto-apply pair integrations.
|
|
76
|
-
- Pair integrations are applied explicitly via `pnpm forgeon:sync-integrations`.
|
|
77
|
-
- Modules must not assume `db-prisma` is present unless they explicitly require it; DB integrations should be optional and synced when DB is added later.
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# PROJECT
|
|
2
|
-
|
|
3
|
-
## What This Repository Is
|
|
4
|
-
|
|
5
|
-
A canonical fullstack monorepo scaffold intended to be reused as a project starter.
|
|
6
|
-
|
|
7
|
-
## Structure
|
|
8
|
-
|
|
9
|
-
- `apps/api` - NestJS backend
|
|
10
|
-
- `apps/web` - React frontend (fixed stack)
|
|
11
|
-
- `packages/core` - shared backend core package (`core-config`, `core-errors`, `core-validation`)
|
|
12
|
-
- `packages/db-prisma` - reusable Prisma/Postgres module (`DbPrismaModule`, `PrismaService`, config)
|
|
13
|
-
- `packages/i18n` - reusable nestjs-i18n integration package
|
|
14
|
-
- `infra` - Docker Compose + proxy preset (`caddy|nginx|none`)
|
|
15
|
-
- `resources/i18n` - translation dictionaries
|
|
16
|
-
- `docs` - documentation, AI prompts, and module contracts
|
|
17
|
-
|
|
18
|
-
## Run Modes
|
|
19
|
-
|
|
20
|
-
### Dev mode
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
pnpm install
|
|
24
|
-
pnpm dev
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Docker mode
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
docker compose --env-file infra/docker/.env.example -f infra/docker/compose.yml up --build
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
The API uses Prisma and expects `DATABASE_URL` from env.
|
|
34
|
-
|
|
35
|
-
If proxy preset is `none`, API is directly available on `localhost:3000`.
|
|
36
|
-
|
|
37
|
-
## Error Handling
|
|
38
|
-
|
|
39
|
-
`core-errors` is enabled by default.
|
|
40
|
-
|
|
41
|
-
- `CoreErrorsModule` is imported in `apps/api/src/app.module.ts`.
|
|
42
|
-
- `CoreExceptionFilter` is registered globally in `apps/api/src/main.ts`.
|
|
43
|
-
- Throw standard Nest exceptions from controllers/services; the filter converts them to a stable envelope.
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
# ROADMAP
|
|
2
|
-
|
|
3
|
-
This is a living plan. Scope and priorities may change.
|
|
4
|
-
|
|
5
|
-
## Current Foundation (Implemented)
|
|
6
|
-
|
|
7
|
-
- [x] Canonical scaffold: NestJS API + React web + Docker (+ default-on `db-prisma` module)
|
|
8
|
-
- [x] Proxy preset selection: `caddy | nginx | none`
|
|
9
|
-
- [x] `@forgeon/core`:
|
|
10
|
-
- [x] `core-config` (typed env config + validation)
|
|
11
|
-
- [x] `core-errors` (global envelope + exception filter)
|
|
12
|
-
- [x] `core-validation` (global validation pipe)
|
|
13
|
-
- [x] `@forgeon/db-prisma` as default-applied DB module
|
|
14
|
-
- [x] i18n add-module baseline:
|
|
15
|
-
- [x] `@forgeon/i18n`, `@forgeon/i18n-contracts`, `@forgeon/i18n-web`
|
|
16
|
-
- [x] shared dictionaries in `resources/i18n/*`
|
|
17
|
-
- [x] tooling: `i18n:sync`, `i18n:check`, `i18n:types`, `i18n:add`
|
|
18
|
-
- [x] module diagnostics probes pattern (`/api/health/*` + web test buttons)
|
|
19
|
-
|
|
20
|
-
## Standards (Accepted)
|
|
21
|
-
|
|
22
|
-
- [x] `*-contracts` and `*-web` packages are ESM-first
|
|
23
|
-
- [x] API runtime modules use Node-oriented TS config
|
|
24
|
-
- [x] no cross-package imports via `/src/*`; only package entrypoints
|
|
25
|
-
|
|
26
|
-
## Updated Priority Backlog
|
|
27
|
-
|
|
28
|
-
### P0 (Immediate Must-Have)
|
|
29
|
-
|
|
30
|
-
- [ ] `logger`
|
|
31
|
-
- [ ] canonical logger module
|
|
32
|
-
- [ ] requestId / correlationId propagation
|
|
33
|
-
- [ ] structured log conventions
|
|
34
|
-
|
|
35
|
-
- [ ] `openapi / swagger`
|
|
36
|
-
- [ ] env toggle: `SWAGGER_ENABLED`
|
|
37
|
-
- [ ] standard setup
|
|
38
|
-
- [ ] bearer integration hook for jwt-auth
|
|
39
|
-
- [ ] `/docs` route
|
|
40
|
-
|
|
41
|
-
- [ ] `jwt-auth`
|
|
42
|
-
- [ ] module split: contracts/api/web
|
|
43
|
-
- [ ] access + refresh baseline
|
|
44
|
-
- [ ] guards/strategy integration
|
|
45
|
-
|
|
46
|
-
- [ ] `rbac / permissions`
|
|
47
|
-
- [ ] decorators: `@Roles()`, `@Permissions()`
|
|
48
|
-
- [ ] guard + policy helper
|
|
49
|
-
- [ ] contracts: `Role`, `Permission`
|
|
50
|
-
- [ ] integration with jwt-auth claims
|
|
51
|
-
|
|
52
|
-
- [ ] `redis/queue foundation`
|
|
53
|
-
- [ ] base Redis config/service
|
|
54
|
-
- [ ] queue baseline (BullMQ or equivalent)
|
|
55
|
-
- [ ] retry and dead-letter conventions
|
|
56
|
-
|
|
57
|
-
- [ ] `rate-limit`
|
|
58
|
-
- [ ] Nest Throttler add-module
|
|
59
|
-
- [ ] policies: route / user / ip
|
|
60
|
-
- [ ] error code: `TOO_MANY_REQUESTS`
|
|
61
|
-
- [ ] reverse-proxy-aware mode (`trust proxy`)
|
|
62
|
-
|
|
63
|
-
- [ ] `files` (upload + storage)
|
|
64
|
-
- [ ] upload endpoints + DTO + guards
|
|
65
|
-
- [ ] storage presets: local + S3-compatible (MinIO/R2)
|
|
66
|
-
- [ ] MIME/size validation
|
|
67
|
-
- [ ] optional image processing subpackage (`sharp`)
|
|
68
|
-
- [ ] error codes: `UPLOAD_INVALID_TYPE`, `UPLOAD_TOO_LARGE`, `UPLOAD_QUOTA`
|
|
69
|
-
|
|
70
|
-
### P1 (Strongly Recommended)
|
|
71
|
-
|
|
72
|
-
- [ ] `testing baseline`
|
|
73
|
-
- [ ] unit + e2e presets
|
|
74
|
-
- [ ] test helpers for add-modules
|
|
75
|
-
- [ ] smoke test template for generated project
|
|
76
|
-
|
|
77
|
-
- [ ] `CI quality gates`
|
|
78
|
-
- [ ] `typecheck`, `lint`, `test`, docker build smoke
|
|
79
|
-
- [ ] release gate checklist
|
|
80
|
-
|
|
81
|
-
- [ ] `cache` (Redis)
|
|
82
|
-
- [ ] CacheModule preset
|
|
83
|
-
- [ ] key naming conventions
|
|
84
|
-
- [ ] shared wrapper/service
|
|
85
|
-
|
|
86
|
-
- [ ] `scheduler`
|
|
87
|
-
- [ ] `@nestjs/schedule` integration
|
|
88
|
-
- [ ] task template
|
|
89
|
-
- [ ] optional distributed lock (Redis)
|
|
90
|
-
|
|
91
|
-
- [ ] `mail`
|
|
92
|
-
- [ ] at least one provider preset (SMTP/Resend/SendGrid)
|
|
93
|
-
- [ ] templates: verify email, reset password
|
|
94
|
-
- [ ] optional outbox with queue
|
|
95
|
-
|
|
96
|
-
- [ ] workspace `eslint/prettier` config package
|
|
97
|
-
|
|
98
|
-
### P2 (Later)
|
|
99
|
-
|
|
100
|
-
- [ ] frontend `http-client` module
|
|
101
|
-
- [ ] frontend UI kit package
|
|
102
|
-
- [ ] migrate reusable parts from `eso-dt` (when available)
|
|
103
|
-
- [ ] extend missing primitives
|
|
104
|
-
- [ ] `realtime` (ws)
|
|
105
|
-
- [ ] gateway baseline
|
|
106
|
-
- [ ] jwt auth for ws
|
|
107
|
-
- [ ] rooms + basic events
|
|
108
|
-
- [ ] `webhooks` module (subject to scope validation)
|
|
109
|
-
- [ ] signed inbound verify (HMAC)
|
|
110
|
-
- [ ] signed outbound sender
|
|
111
|
-
- [ ] replay protection (timestamp/nonce)
|
|
112
|
-
|
|
113
|
-
## Execution Plan (3 Sprints)
|
|
114
|
-
|
|
115
|
-
### Sprint 1: Platform Baseline and Security Start
|
|
116
|
-
|
|
117
|
-
Scope:
|
|
118
|
-
- `logger`
|
|
119
|
-
- `openapi/swagger`
|
|
120
|
-
- `jwt-auth`
|
|
121
|
-
- `testing baseline`
|
|
122
|
-
- `CI quality gates`
|
|
123
|
-
|
|
124
|
-
Definition of Done:
|
|
125
|
-
- add-modules install cleanly via `create-forgeon add <module>`
|
|
126
|
-
- local dev (`pnpm dev`) and docker build both pass on fresh generated project
|
|
127
|
-
- each module has probe endpoint and web probe UI hook when applicable
|
|
128
|
-
- docs updated in both root and template docs
|
|
129
|
-
|
|
130
|
-
### Sprint 2: Authorization and Traffic Control
|
|
131
|
-
|
|
132
|
-
Scope:
|
|
133
|
-
- `rbac/permissions`
|
|
134
|
-
- `redis/queue foundation`
|
|
135
|
-
- `rate-limit`
|
|
136
|
-
- `files`
|
|
137
|
-
- `cache`
|
|
138
|
-
|
|
139
|
-
Definition of Done:
|
|
140
|
-
- claims/roles/permissions flow validated end-to-end (api + web contracts)
|
|
141
|
-
- rate-limit and files include standardized error codes and envelope mapping
|
|
142
|
-
- Redis-backed modules run in docker profile with documented env keys
|
|
143
|
-
- at least one e2e happy-path per module
|
|
144
|
-
|
|
145
|
-
### Sprint 3: Async Integrations and Frontend Foundation
|
|
146
|
-
|
|
147
|
-
Scope:
|
|
148
|
-
- `scheduler`
|
|
149
|
-
- `mail`
|
|
150
|
-
- workspace `eslint/prettier` config package
|
|
151
|
-
- frontend `http-client`
|
|
152
|
-
|
|
153
|
-
Definition of Done:
|
|
154
|
-
- queue/scheduler/mail basic scenarios work in local + docker
|
|
155
|
-
- frontend http-client consumes api contracts with typed errors
|
|
156
|
-
- lint/typecheck/test/build pass through CI gate preset
|
|
157
|
-
- docs include migration notes and extension points
|
|
158
|
-
|
|
159
|
-
## Explicit Dependencies and Order Constraints
|
|
160
|
-
|
|
161
|
-
- `rbac` depends on `jwt-auth`
|
|
162
|
-
- `rate-limit` should follow Redis/queue foundation for scalable mode
|
|
163
|
-
- `mail` should reuse queue foundation where possible
|
|
164
|
-
- `openapi` is most useful before/with `jwt-auth` and `http-client`
|
|
165
|
-
- `realtime` and `webhooks` stay post-MVP unless a concrete use-case appears
|
|
166
|
-
|
|
167
|
-
## i18n Policy For Add-Modules
|
|
168
|
-
|
|
169
|
-
- [ ] each add-module that introduces user-facing text defines its own namespace templates
|
|
170
|
-
- [ ] if i18n is already enabled, namespace files are added during module installation
|
|
171
|
-
- [ ] if module is installed first and i18n later, namespaces are merged during i18n installation
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# TASKS
|
|
2
|
-
|
|
3
|
-
## Feature Discovery Matrix
|
|
4
|
-
|
|
5
|
-
```text
|
|
6
|
-
Scan this monorepo and build a backend feature matrix by app/package.
|
|
7
|
-
Use only evidence from code and dependencies.
|
|
8
|
-
Output:
|
|
9
|
-
1) taxonomy by category
|
|
10
|
-
2) feature comparison table
|
|
11
|
-
3) common core
|
|
12
|
-
4) unique features
|
|
13
|
-
5) architectural inconsistencies
|
|
14
|
-
Include file references for every feature.
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Add Module Package
|
|
18
|
-
|
|
19
|
-
```text
|
|
20
|
-
Create a new reusable package under packages/ for <feature-name>.
|
|
21
|
-
Requirements:
|
|
22
|
-
- minimal API
|
|
23
|
-
- NestJS-compatible module
|
|
24
|
-
- docs in package README
|
|
25
|
-
- wire into apps/api conditionally via env flag
|
|
26
|
-
- keep backward compatibility
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Refactor Core
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
Move shared backend logic from apps/api into packages/core.
|
|
33
|
-
Do not change behavior.
|
|
34
|
-
Update imports, package dependencies, and docs.
|
|
35
|
-
Run build checks and show changed files.
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Generate Preset
|
|
39
|
-
|
|
40
|
-
```text
|
|
41
|
-
Create or update create-forgeon preset flow:
|
|
42
|
-
- keep canonical stack fixed: NestJS + React + Prisma/Postgres + Docker
|
|
43
|
-
- allow only runtime proxy choice: caddy/nginx/none
|
|
44
|
-
- update generated docs fragments
|
|
45
|
-
- update docs/AI/ARCHITECTURE.md and docs/AI/MODULE_SPEC.md when scope changes
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Add Fullstack Module
|
|
49
|
-
|
|
50
|
-
```text
|
|
51
|
-
Implement `create-forgeon add <module-id>` for a fullstack feature.
|
|
52
|
-
Requirements:
|
|
53
|
-
- split module into contracts/api/web packages
|
|
54
|
-
- contracts is source of truth for routes, DTOs, errors
|
|
55
|
-
- if feasible, add module probe hooks in API (`/api/health/*`) and web diagnostics UI
|
|
56
|
-
- if i18n is enabled, add module namespace files and wire them for both API and web
|
|
57
|
-
- add docs note under docs/AI/MODULES/<module-id>.md
|
|
58
|
-
- keep backward compatibility
|
|
59
|
-
```
|
|
60
|
-
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# VALIDATION
|
|
2
|
-
|
|
3
|
-
## Backend DTO Validation Standard
|
|
4
|
-
|
|
5
|
-
- Use `class-validator` decorators on DTO classes.
|
|
6
|
-
- Global validation is centralized in `@forgeon/core` via `createValidationPipe()`.
|
|
7
|
-
- Current defaults:
|
|
8
|
-
- `whitelist: true`
|
|
9
|
-
- `transform: true`
|
|
10
|
-
- `validationError.target: false`
|
|
11
|
-
- `validationError.value: false`
|
|
12
|
-
- Keep DTO validation messages stable and explicit.
|
|
13
|
-
- For required values, use a consistent key or message pattern.
|
|
14
|
-
|
|
15
|
-
## Config Validation Standard
|
|
16
|
-
|
|
17
|
-
- Use Zod for env/config validation.
|
|
18
|
-
- Core env is validated by `@forgeon/core` (`core-config`).
|
|
19
|
-
- Each add-module validates only its own env keys with its own Zod schema.
|
|
20
|
-
- Avoid one global env schema for all modules; keep schemas modular.
|
|
21
|
-
|
|
22
|
-
## Error Details for Validation
|
|
23
|
-
|
|
24
|
-
- Error envelope stays consistent:
|
|
25
|
-
- `error.code`
|
|
26
|
-
- `error.message`
|
|
27
|
-
- `error.status`
|
|
28
|
-
- optional `error.details`
|
|
29
|
-
- Validation details should be structured (not `any`).
|
|
30
|
-
- `core-validation` formats validation details as:
|
|
31
|
-
- `{ field?: string, message: string }[]`
|