codeninja 2.0.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/.gitattributes +11 -0
- package/README.md +293 -0
- package/agent/database-agent.md +504 -0
- package/agent/designs/README.md +10 -0
- package/agent/global-agent.md +236 -0
- package/agent/nodejs-agent.md +406 -0
- package/agent/reactjs-agent.md +260 -0
- package/cli.js +352 -0
- package/commands/audit.workflow.md +111 -0
- package/commands/create-api.workflow.md +99 -0
- package/commands/db-add-index.workflow.md +97 -0
- package/commands/db-create-table.workflow.md +132 -0
- package/commands/db-drop-table.workflow.md +103 -0
- package/commands/db-modify-table.workflow.md +159 -0
- package/commands/db-seed.workflow.md +99 -0
- package/commands/db-sync.workflow.md +100 -0
- package/commands/design.workflow.md +66 -0
- package/commands/initialize-project.workflow.md +500 -0
- package/commands/integrate-api.workflow.md +448 -0
- package/commands/modularize.workflow.md +329 -0
- package/commands/refactor.workflow.md +70 -0
- package/commands/sync.workflow.md +962 -0
- package/commands/test.workflow.md +40 -0
- package/commands/validate-page.workflow.md +543 -0
- package/mcp-server.js +842 -0
- package/package.json +24 -0
- package/tasks/README.md +283 -0
- package/tasks/add-health-route.task.md +103 -0
- package/tasks/ask-api-integration-scope.task.md +34 -0
- package/tasks/ask-api-key.task.md +23 -0
- package/tasks/ask-api-version.task.md +28 -0
- package/tasks/ask-client-type.task.md +24 -0
- package/tasks/ask-column-enum-values.task.md +51 -0
- package/tasks/ask-column-is-enum.task.md +39 -0
- package/tasks/ask-column-name.task.md +39 -0
- package/tasks/ask-column-position.task.md +39 -0
- package/tasks/ask-column-type.task.md +59 -0
- package/tasks/ask-database-config.task.md +66 -0
- package/tasks/ask-database-host.task.md +16 -0
- package/tasks/ask-database-name.task.md +18 -0
- package/tasks/ask-database-port.task.md +23 -0
- package/tasks/ask-database-type.task.md +30 -0
- package/tasks/ask-database-user.task.md +14 -0
- package/tasks/ask-design-description.task.md +16 -0
- package/tasks/ask-design-target.task.md +24 -0
- package/tasks/ask-encrypted-transport.task.md +25 -0
- package/tasks/ask-encryption-iv.task.md +23 -0
- package/tasks/ask-encryption-key.task.md +23 -0
- package/tasks/ask-feature-name.task.md +20 -0
- package/tasks/ask-http-method.task.md +21 -0
- package/tasks/ask-index-columns.task.md +46 -0
- package/tasks/ask-index-file-placement.task.md +33 -0
- package/tasks/ask-index-sort-order.task.md +37 -0
- package/tasks/ask-index-type.task.md +42 -0
- package/tasks/ask-init-mode.task.md +28 -0
- package/tasks/ask-linked-service.task.md +57 -0
- package/tasks/ask-modify-operation.task.md +36 -0
- package/tasks/ask-modularize-scope.task.md +31 -0
- package/tasks/ask-module-name.task.md +30 -0
- package/tasks/ask-new-column-name.task.md +21 -0
- package/tasks/ask-new-table-name.task.md +22 -0
- package/tasks/ask-old-column-name.task.md +22 -0
- package/tasks/ask-package-author.task.md +16 -0
- package/tasks/ask-package-name.task.md +23 -0
- package/tasks/ask-page-path.task.md +40 -0
- package/tasks/ask-primary-table.task.md +30 -0
- package/tasks/ask-project-figma.task.md +71 -0
- package/tasks/ask-project-info-doc.task.md +57 -0
- package/tasks/ask-project-scope-of-work.task.md +57 -0
- package/tasks/ask-project-type.task.md +24 -0
- package/tasks/ask-react-target-service.task.md +32 -0
- package/tasks/ask-redis-config.task.md +42 -0
- package/tasks/ask-redis-host.task.md +16 -0
- package/tasks/ask-redis-port.task.md +18 -0
- package/tasks/ask-refactor-type.task.md +26 -0
- package/tasks/ask-requires-auth.task.md +22 -0
- package/tasks/ask-response-mode.task.md +38 -0
- package/tasks/ask-route-description.task.md +20 -0
- package/tasks/ask-route-path.task.md +29 -0
- package/tasks/ask-seed-row-values.task.md +42 -0
- package/tasks/ask-seed-rows-count.task.md +22 -0
- package/tasks/ask-service-description.task.md +16 -0
- package/tasks/ask-service-name.task.md +27 -0
- package/tasks/ask-service-port.task.md +24 -0
- package/tasks/ask-supported-languages.task.md +40 -0
- package/tasks/ask-table-file-number.task.md +36 -0
- package/tasks/ask-table-indexes.task.md +47 -0
- package/tasks/ask-table-name.task.md +32 -0
- package/tasks/ask-table-needs-soft-delete.task.md +29 -0
- package/tasks/ask-table-needs-status.task.md +30 -0
- package/tasks/ask-table-purpose.task.md +28 -0
- package/tasks/ask-table-seed-data.task.md +44 -0
- package/tasks/ask-target-service.task.md +32 -0
- package/tasks/ask-test-type.task.md +20 -0
- package/tasks/ask-validation-library.task.md +38 -0
- package/tasks/detect-repository-state.task.md +92 -0
- package/tasks/generate-app.task.md +146 -0
- package/tasks/generate-common.task.md +330 -0
- package/tasks/generate-constants.task.md +123 -0
- package/tasks/generate-database.task.md +168 -0
- package/tasks/generate-docker-compose.task.md +298 -0
- package/tasks/generate-dockerfile.task.md +126 -0
- package/tasks/generate-dockerignore.task.md +123 -0
- package/tasks/generate-enc-dec-html.task.md +127 -0
- package/tasks/generate-enc-dec-php.task.md +145 -0
- package/tasks/generate-encryption.task.md +159 -0
- package/tasks/generate-fast-defaults.task.md +68 -0
- package/tasks/generate-gitignore.task.md +79 -0
- package/tasks/generate-headerValidator.task.md +377 -0
- package/tasks/generate-ide-configs.task.md +114 -0
- package/tasks/generate-ioRedis.task.md +120 -0
- package/tasks/generate-language-en.task.md +155 -0
- package/tasks/generate-logging.task.md +257 -0
- package/tasks/generate-model.task.md +180 -0
- package/tasks/generate-notification.task.md +251 -0
- package/tasks/generate-package-json.task.md +114 -0
- package/tasks/generate-rateLimiter.task.md +125 -0
- package/tasks/generate-react-api-client.task.md +169 -0
- package/tasks/generate-react-api-handler.task.md +102 -0
- package/tasks/generate-react-app-jsx.task.md +56 -0
- package/tasks/generate-react-dockerfile.task.md +175 -0
- package/tasks/generate-react-env.task.md +58 -0
- package/tasks/generate-react-gitignore.task.md +49 -0
- package/tasks/generate-react-htaccess.task.md +54 -0
- package/tasks/generate-react-index-html.task.md +53 -0
- package/tasks/generate-react-index-jsx.task.md +51 -0
- package/tasks/generate-react-package-json.task.md +77 -0
- package/tasks/generate-react-welcome-page.task.md +71 -0
- package/tasks/generate-readme.task.md +160 -0
- package/tasks/generate-response.task.md +202 -0
- package/tasks/generate-route-manager.task.md +173 -0
- package/tasks/generate-route.task.md +203 -0
- package/tasks/generate-swagger.task.md +290 -0
- package/tasks/generate-tbl-user-deviceinfo.task.md +75 -0
- package/tasks/generate-template.task.md +129 -0
- package/tasks/generate-validator.task.md +122 -0
- package/tasks/show-db-table-summary.task.md +66 -0
- package/tasks/show-final-summary.task.md +108 -0
- package/tasks/show-init-summary.task.md +257 -0
- package/tasks/write-context.task.md +314 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-database
|
|
4
|
+
agent: nodejs-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: config/database.js
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Establishes and exports the database connection for the service. All
|
|
11
|
+
model files import this module to run queries. The implementation differs
|
|
12
|
+
based on `context.db.type` — one file is generated per project, not one
|
|
13
|
+
per database type. The agent reads the db type from context and generates
|
|
14
|
+
the correct implementation.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Dependencies to Import
|
|
19
|
+
|
|
20
|
+
- `../logger/logging` — imported as `log`. Used for connection error
|
|
21
|
+
logging and query error logging.
|
|
22
|
+
|
|
23
|
+
Additionally, one database driver is imported based on `context.db.type`:
|
|
24
|
+
- If `postgresql` → import `{ Pool }` from `pg`
|
|
25
|
+
- If `mysql` → import `mysql2/promise` and create a pool using
|
|
26
|
+
`mysql2.createPool()`
|
|
27
|
+
- If `mongodb` → import `mongoose`
|
|
28
|
+
|
|
29
|
+
Never import more than one driver. Only the driver matching
|
|
30
|
+
`context.db.type` is imported.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Implementation by Database Type
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### PostgreSQL (context.db.type == "postgresql")
|
|
39
|
+
|
|
40
|
+
**Pool creation**:
|
|
41
|
+
Create a `new Pool` instance with these configuration keys all read
|
|
42
|
+
from process.env:
|
|
43
|
+
- `user` from `process.env.DATABASE_USER`
|
|
44
|
+
- `host` from `process.env.DATABASE_HOST`
|
|
45
|
+
- `database` from `process.env.DATABASE_NAME`
|
|
46
|
+
- `password` from `process.env.DATABASE_PASSWORD`
|
|
47
|
+
- `port` from `process.env.DATABASE_PORT`
|
|
48
|
+
- `ssl` set to `{ rejectUnauthorized: false }` — required for most
|
|
49
|
+
hosted PostgreSQL providers including AWS RDS and Supabase.
|
|
50
|
+
|
|
51
|
+
**Pool error handler**:
|
|
52
|
+
Register a listener on the pool's `error` event using `pool.on('error',
|
|
53
|
+
callback)`. The callback receives the error object. Inside the callback:
|
|
54
|
+
log at critical level with the message "Error establishing PostgreSQL
|
|
55
|
+
connection" and the error object. Then call `process.exit(-1)`.
|
|
56
|
+
This handles unexpected errors on idle clients in the pool — without
|
|
57
|
+
this handler, such errors would be unhandled rejections and crash the
|
|
58
|
+
process silently.
|
|
59
|
+
|
|
60
|
+
**logFormattedQuery(queryText, params)**:
|
|
61
|
+
Internal helper function. Not exported.
|
|
62
|
+
Purpose: reconstructs the SQL query string with parameter placeholders
|
|
63
|
+
replaced by their actual values, for use in error logs only. Never used
|
|
64
|
+
to actually execute queries — parameterized queries are always used for
|
|
65
|
+
execution.
|
|
66
|
+
Takes `queryText` (the SQL string with `$1`, `$2` placeholders) and
|
|
67
|
+
`params` (the values array).
|
|
68
|
+
Uses a regex replace on the pattern `\$(\d+)` to find each placeholder.
|
|
69
|
+
For each match, read the corresponding value from params at index
|
|
70
|
+
`(matched_number - 1)`.
|
|
71
|
+
Format the value:
|
|
72
|
+
- If null or undefined → replace with the string `NULL`
|
|
73
|
+
- If a string → wrap in single quotes: `'value'`
|
|
74
|
+
- Any other type → use the value as-is (numbers, booleans)
|
|
75
|
+
Returns the reconstructed query string. Used only in error log messages.
|
|
76
|
+
|
|
77
|
+
**query(text, params)**:
|
|
78
|
+
Async function. The primary export used by all model files.
|
|
79
|
+
Parameters:
|
|
80
|
+
- `text` — the SQL query string with `$1`, `$2` parameter placeholders
|
|
81
|
+
- `params` — array of values. Defaults to empty array if not provided.
|
|
82
|
+
|
|
83
|
+
Flow:
|
|
84
|
+
1. Call `pool.query(text, params)` inside a try/catch.
|
|
85
|
+
2. On success — return the result object directly. The result has
|
|
86
|
+
`rows` (array of row objects) and `rowCount` (number of affected
|
|
87
|
+
rows) which callers destructure as needed.
|
|
88
|
+
3. On error — log at error level with the message "DATABASE QUERY ERROR",
|
|
89
|
+
including the formatted query from `logFormattedQuery(text, params)`
|
|
90
|
+
and the error object. Then re-throw the error. Model functions are
|
|
91
|
+
responsible for catching query errors.
|
|
92
|
+
|
|
93
|
+
**Export**:
|
|
94
|
+
```
|
|
95
|
+
module.exports = { query, pool }
|
|
96
|
+
```
|
|
97
|
+
`pool` is exported for use in transaction scenarios where the caller
|
|
98
|
+
needs to acquire a client directly and manage BEGIN/COMMIT/ROLLBACK
|
|
99
|
+
manually.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### MySQL (context.db.type == "mysql")
|
|
104
|
+
|
|
105
|
+
**Pool creation**:
|
|
106
|
+
Call `mysql2.createPool()` with these configuration keys all from
|
|
107
|
+
process.env:
|
|
108
|
+
- `host` from `process.env.DATABASE_HOST`
|
|
109
|
+
- `user` from `process.env.DATABASE_USER`
|
|
110
|
+
- `password` from `process.env.DATABASE_PASSWORD`
|
|
111
|
+
- `database` from `process.env.DATABASE_NAME`
|
|
112
|
+
- `port` from `process.env.DATABASE_PORT`
|
|
113
|
+
- `waitForConnections: true`
|
|
114
|
+
- `connectionLimit: 10`
|
|
115
|
+
- `queueLimit: 0`
|
|
116
|
+
|
|
117
|
+
**logFormattedQuery(queryText, params)**:
|
|
118
|
+
Same purpose as PostgreSQL version but uses `?` placeholders instead
|
|
119
|
+
of `$1` style. Replace each `?` in sequence with its corresponding
|
|
120
|
+
param value, applying the same null/string/other formatting rules.
|
|
121
|
+
|
|
122
|
+
**query(text, params)**:
|
|
123
|
+
Same async pattern as PostgreSQL. Call `pool.execute(text, params)`.
|
|
124
|
+
Returns `[rows, fields]` — destructure and return `{ rows, rowCount:
|
|
125
|
+
rows.length }` to give callers a consistent interface matching the
|
|
126
|
+
PostgreSQL result shape.
|
|
127
|
+
On error — same log pattern and re-throw.
|
|
128
|
+
|
|
129
|
+
**Export**:
|
|
130
|
+
```
|
|
131
|
+
module.exports = { query, pool }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### MongoDB (context.db.type == "mongodb")
|
|
137
|
+
|
|
138
|
+
**Connection**:
|
|
139
|
+
Call `mongoose.connect()` with the connection string constructed from
|
|
140
|
+
process.env values:
|
|
141
|
+
`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}
|
|
142
|
+
@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/
|
|
143
|
+
${process.env.DATABASE_NAME}`
|
|
144
|
+
Options: `{ useNewUrlParser: true, useUnifiedTopology: true }`
|
|
145
|
+
|
|
146
|
+
Wrap in an async immediately-invoked function. On connection success —
|
|
147
|
+
log at info level. On error — log at critical level and call
|
|
148
|
+
`process.exit(-1)`.
|
|
149
|
+
|
|
150
|
+
**Export**:
|
|
151
|
+
```
|
|
152
|
+
module.exports = mongoose
|
|
153
|
+
```
|
|
154
|
+
Model files import mongoose directly and define their own schemas.
|
|
155
|
+
No `query` wrapper is needed for MongoDB — each model uses mongoose
|
|
156
|
+
model methods directly.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## What This File Does NOT Do
|
|
161
|
+
|
|
162
|
+
- Does not hardcode any connection values — all from process.env
|
|
163
|
+
- Does not define any data models or schemas — those live in
|
|
164
|
+
module _model.js files
|
|
165
|
+
- Does not retry failed connections — process exits on critical
|
|
166
|
+
connection failure
|
|
167
|
+
- Does not export raw connection strings — only the pool or
|
|
168
|
+
connection object
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-docker-compose
|
|
4
|
+
agent: global-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: docker-compose.yml
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Orchestrates all services in the project (NodeJS APIs, ReactJS frontends,
|
|
11
|
+
databases, Redis) in a unified Docker environment. Generated once per
|
|
12
|
+
repository and updated as new services are added.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Generation Logic
|
|
17
|
+
|
|
18
|
+
### When to Generate
|
|
19
|
+
- First `@initialize-project` in the repo (no docker-compose.yml exists)
|
|
20
|
+
- Any subsequent `@initialize-project` that adds a new service
|
|
21
|
+
|
|
22
|
+
### When to Update
|
|
23
|
+
- New service initialized → add service definition
|
|
24
|
+
- Service port changed → update ports mapping
|
|
25
|
+
- Database type changed → update db service definition
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Template Structure
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
version: '3.8'
|
|
33
|
+
|
|
34
|
+
services:
|
|
35
|
+
# Database service (if context.db.type exists)
|
|
36
|
+
{db_service_name}:
|
|
37
|
+
image: {db_image}
|
|
38
|
+
container_name: {project_name}-{db_type}
|
|
39
|
+
environment:
|
|
40
|
+
{db_env_vars}
|
|
41
|
+
ports:
|
|
42
|
+
- "{db_port}:{db_internal_port}"
|
|
43
|
+
volumes:
|
|
44
|
+
- {db_data_volume}:/var/lib/{db_volume_path}
|
|
45
|
+
- ./database/{db_type}/migrations:/docker-entrypoint-initdb.d:ro
|
|
46
|
+
networks:
|
|
47
|
+
- {project_name}-network
|
|
48
|
+
healthcheck:
|
|
49
|
+
test: {db_healthcheck}
|
|
50
|
+
interval: 10s
|
|
51
|
+
timeout: 5s
|
|
52
|
+
retries: 5
|
|
53
|
+
|
|
54
|
+
# Redis service (always included for NodeJS services)
|
|
55
|
+
redis:
|
|
56
|
+
image: redis:7-alpine
|
|
57
|
+
container_name: {project_name}-redis
|
|
58
|
+
ports:
|
|
59
|
+
- "6379:6379"
|
|
60
|
+
volumes:
|
|
61
|
+
- redis-data:/data
|
|
62
|
+
networks:
|
|
63
|
+
- {project_name}-network
|
|
64
|
+
command: redis-server --appendonly yes
|
|
65
|
+
healthcheck:
|
|
66
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
67
|
+
interval: 10s
|
|
68
|
+
timeout: 3s
|
|
69
|
+
retries: 5
|
|
70
|
+
|
|
71
|
+
# NodeJS services (one per service in context.services where type == nodejs)
|
|
72
|
+
{service_name}:
|
|
73
|
+
build:
|
|
74
|
+
context: ./{service_name}
|
|
75
|
+
dockerfile: Dockerfile
|
|
76
|
+
container_name: {project_name}-{service_name}
|
|
77
|
+
environment:
|
|
78
|
+
- NODE_ENV=production
|
|
79
|
+
- DB_HOST={db_service_name}
|
|
80
|
+
- DB_PORT={db_internal_port}
|
|
81
|
+
- DB_NAME=${DB_NAME}
|
|
82
|
+
- DB_USER=${DB_USER}
|
|
83
|
+
- DB_PASS=${DB_PASS}
|
|
84
|
+
- REDIS_HOST=redis
|
|
85
|
+
- REDIS_PORT=6379
|
|
86
|
+
- KEY=${KEY}
|
|
87
|
+
- IV=${IV}
|
|
88
|
+
- API_KEY=${API_KEY}
|
|
89
|
+
ports:
|
|
90
|
+
- "{service_port}:{service_port}"
|
|
91
|
+
volumes:
|
|
92
|
+
- ./{service_name}/logger/logs:/app/logger/logs
|
|
93
|
+
- ./{service_name}/pem:/app/pem
|
|
94
|
+
- ./{service_name}/images:/app/images
|
|
95
|
+
depends_on:
|
|
96
|
+
{db_service_name}:
|
|
97
|
+
condition: service_healthy
|
|
98
|
+
redis:
|
|
99
|
+
condition: service_healthy
|
|
100
|
+
networks:
|
|
101
|
+
- {project_name}-network
|
|
102
|
+
restart: unless-stopped
|
|
103
|
+
|
|
104
|
+
# ReactJS services (one per service in context.services where type == reactjs)
|
|
105
|
+
{frontend_service_name}:
|
|
106
|
+
build:
|
|
107
|
+
context: ./{frontend_service_name}
|
|
108
|
+
dockerfile: Dockerfile
|
|
109
|
+
container_name: {project_name}-{frontend_service_name}
|
|
110
|
+
environment:
|
|
111
|
+
- REACT_APP_BASE_URL=http://{linked_service_name}:{linked_service_port}/api/v1/
|
|
112
|
+
- REACT_APP_API_KEY=${REACT_APP_API_KEY}
|
|
113
|
+
- REACT_APP_KEY=${REACT_APP_KEY}
|
|
114
|
+
- REACT_APP_IV=${REACT_APP_IV}
|
|
115
|
+
ports:
|
|
116
|
+
- "{frontend_port}:80"
|
|
117
|
+
depends_on:
|
|
118
|
+
- {linked_service_name}
|
|
119
|
+
networks:
|
|
120
|
+
- {project_name}-network
|
|
121
|
+
restart: unless-stopped
|
|
122
|
+
|
|
123
|
+
networks:
|
|
124
|
+
{project_name}-network:
|
|
125
|
+
driver: bridge
|
|
126
|
+
|
|
127
|
+
volumes:
|
|
128
|
+
{db_data_volume}:
|
|
129
|
+
redis-data:
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Variable Substitution Rules
|
|
135
|
+
|
|
136
|
+
### Project-level
|
|
137
|
+
- `{project_name}` → `context.project_name` (lowercase, hyphens)
|
|
138
|
+
|
|
139
|
+
### Database service
|
|
140
|
+
- `{db_service_name}` → `postgres` | `mysql` | `mongo`
|
|
141
|
+
- `{db_image}` → `postgres:15-alpine` | `mysql:8` | `mongo:7`
|
|
142
|
+
- `{db_type}` → `context.db.type`
|
|
143
|
+
- `{db_port}` → `context.db.port`
|
|
144
|
+
- `{db_internal_port}` → 5432 (postgres) | 3306 (mysql) | 27017 (mongo)
|
|
145
|
+
- `{db_data_volume}` → `postgres-data` | `mysql-data` | `mongo-data`
|
|
146
|
+
- `{db_volume_path}` → `postgresql/data` | `mysql` | `mongodb`
|
|
147
|
+
|
|
148
|
+
### Database environment variables
|
|
149
|
+
**PostgreSQL:**
|
|
150
|
+
```yaml
|
|
151
|
+
POSTGRES_DB: ${DB_NAME}
|
|
152
|
+
POSTGRES_USER: ${DB_USER}
|
|
153
|
+
POSTGRES_PASSWORD: ${DB_PASS}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**MySQL:**
|
|
157
|
+
```yaml
|
|
158
|
+
MYSQL_DATABASE: ${DB_NAME}
|
|
159
|
+
MYSQL_USER: ${DB_USER}
|
|
160
|
+
MYSQL_PASSWORD: ${DB_PASS}
|
|
161
|
+
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**MongoDB:**
|
|
165
|
+
```yaml
|
|
166
|
+
MONGO_INITDB_ROOT_USERNAME: ${DB_USER}
|
|
167
|
+
MONGO_INITDB_ROOT_PASSWORD: ${DB_PASS}
|
|
168
|
+
MONGO_INITDB_DATABASE: ${DB_NAME}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Database health checks
|
|
172
|
+
**PostgreSQL:**
|
|
173
|
+
```yaml
|
|
174
|
+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**MySQL:**
|
|
178
|
+
```yaml
|
|
179
|
+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${DB_USER}", "-p${DB_PASS}"]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**MongoDB:**
|
|
183
|
+
```yaml
|
|
184
|
+
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### NodeJS services
|
|
188
|
+
For each service in `context.services` where `type == "nodejs"`:
|
|
189
|
+
- `{service_name}` → service key from context.services
|
|
190
|
+
- `{service_port}` → `context.services[<service_name>].port`
|
|
191
|
+
|
|
192
|
+
### ReactJS services
|
|
193
|
+
For each service in `context.services` where `type == "reactjs"`:
|
|
194
|
+
- `{frontend_service_name}` → service key
|
|
195
|
+
- `{frontend_port}` → `context.services[<service_name>].port`
|
|
196
|
+
- `{linked_service_name}` → `context.services[<service_name>].linked_service`
|
|
197
|
+
- `{linked_service_port}` → `context.services[<linked_service>].port`
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## File Location
|
|
202
|
+
|
|
203
|
+
Write to: `<repository_root>/docker-compose.yml`
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Companion File: .env.docker
|
|
208
|
+
|
|
209
|
+
Also generate `.env.docker` at repository root:
|
|
210
|
+
|
|
211
|
+
```env
|
|
212
|
+
# Database credentials
|
|
213
|
+
DB_NAME={context.db.name}
|
|
214
|
+
DB_USER={context.db.user}
|
|
215
|
+
DB_PASS=your_secure_password_here
|
|
216
|
+
DB_ROOT_PASS=your_root_password_here
|
|
217
|
+
|
|
218
|
+
# Service keys (from first NodeJS service)
|
|
219
|
+
KEY={encryption_key}
|
|
220
|
+
IV={encryption_iv}
|
|
221
|
+
API_KEY={api_key}
|
|
222
|
+
|
|
223
|
+
# ReactJS keys (inherited from linked service)
|
|
224
|
+
REACT_APP_API_KEY={api_key}
|
|
225
|
+
REACT_APP_KEY={encryption_key}
|
|
226
|
+
REACT_APP_IV={encryption_iv}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
And `.env.docker.example` with blanked values.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Update Strategy
|
|
234
|
+
|
|
235
|
+
### If docker-compose.yml already exists:
|
|
236
|
+
1. Read existing file
|
|
237
|
+
2. Parse services section
|
|
238
|
+
3. Add new service definition for the newly initialized service
|
|
239
|
+
4. Update networks and volumes if needed
|
|
240
|
+
5. Write back to disk
|
|
241
|
+
|
|
242
|
+
### If this is the first service:
|
|
243
|
+
Generate the complete file with all sections.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Dependencies
|
|
248
|
+
|
|
249
|
+
- Requires at least one service in `context.services`
|
|
250
|
+
- Requires `context.db` to be populated (for NodeJS/database projects)
|
|
251
|
+
- Can be generated after all service Dockerfiles are created
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Usage Commands (added to README)
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Start all services
|
|
259
|
+
docker-compose --env-file .env.docker up -d
|
|
260
|
+
|
|
261
|
+
# Start specific service
|
|
262
|
+
docker-compose --env-file .env.docker up -d auth
|
|
263
|
+
|
|
264
|
+
# View logs
|
|
265
|
+
docker-compose logs -f auth
|
|
266
|
+
|
|
267
|
+
# Stop all services
|
|
268
|
+
docker-compose down
|
|
269
|
+
|
|
270
|
+
# Rebuild after code changes
|
|
271
|
+
docker-compose build auth
|
|
272
|
+
docker-compose up -d auth
|
|
273
|
+
|
|
274
|
+
# Full reset (removes volumes)
|
|
275
|
+
docker-compose down -v
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Security Notes
|
|
281
|
+
|
|
282
|
+
- `.env.docker` should be in `.gitignore` (secrets never committed)
|
|
283
|
+
- `.env.docker.example` is committed (structure without values)
|
|
284
|
+
- Database passwords must be strong in production
|
|
285
|
+
- Service-to-service communication happens on internal network only
|
|
286
|
+
- External access only through exposed ports
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Production Considerations
|
|
291
|
+
|
|
292
|
+
For production deployment, this compose file should be:
|
|
293
|
+
1. Split into `docker-compose.yml` (base) + `docker-compose.prod.yml` (overrides)
|
|
294
|
+
2. Use Docker secrets instead of environment variables
|
|
295
|
+
3. Add resource limits (CPU, memory)
|
|
296
|
+
4. Add logging drivers
|
|
297
|
+
5. Use external volumes for data persistence
|
|
298
|
+
6. Add traefik/nginx for reverse proxy and SSL
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-dockerfile
|
|
4
|
+
agent: nodejs-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: Dockerfile
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Generates a multi-stage production-ready Dockerfile for the NodeJS service.
|
|
11
|
+
Uses Alpine Linux for minimal image size and includes only production
|
|
12
|
+
dependencies in the final image.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Template
|
|
17
|
+
|
|
18
|
+
```dockerfile
|
|
19
|
+
# Stage 1: Build dependencies
|
|
20
|
+
FROM node:18-alpine AS builder
|
|
21
|
+
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
|
|
24
|
+
# Copy package files
|
|
25
|
+
COPY package*.json ./
|
|
26
|
+
|
|
27
|
+
# Install all dependencies (including devDependencies for build if needed)
|
|
28
|
+
RUN npm ci
|
|
29
|
+
|
|
30
|
+
# Copy application code
|
|
31
|
+
COPY . .
|
|
32
|
+
|
|
33
|
+
# Stage 2: Production image
|
|
34
|
+
FROM node:18-alpine
|
|
35
|
+
|
|
36
|
+
# Install dumb-init for proper signal handling
|
|
37
|
+
RUN apk add --no-cache dumb-init
|
|
38
|
+
|
|
39
|
+
# Create app user for security (don't run as root)
|
|
40
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
41
|
+
adduser -S nodejs -u 1001
|
|
42
|
+
|
|
43
|
+
WORKDIR /app
|
|
44
|
+
|
|
45
|
+
# Copy package files
|
|
46
|
+
COPY package*.json ./
|
|
47
|
+
|
|
48
|
+
# Install only production dependencies
|
|
49
|
+
RUN npm ci --only=production && npm cache clean --force
|
|
50
|
+
|
|
51
|
+
# Copy built application from builder stage
|
|
52
|
+
COPY --from=builder --chown=nodejs:nodejs /app .
|
|
53
|
+
|
|
54
|
+
# Create necessary directories with proper permissions
|
|
55
|
+
RUN mkdir -p logger/logs pem images && \
|
|
56
|
+
chown -R nodejs:nodejs logger/logs pem images
|
|
57
|
+
|
|
58
|
+
# Switch to non-root user
|
|
59
|
+
USER nodejs
|
|
60
|
+
|
|
61
|
+
# Expose the service port
|
|
62
|
+
EXPOSE {port}
|
|
63
|
+
|
|
64
|
+
# Health check
|
|
65
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
|
66
|
+
CMD node -e "require('http').get('http://localhost:{port}/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
|
|
67
|
+
|
|
68
|
+
# Use dumb-init to handle signals properly
|
|
69
|
+
ENTRYPOINT ["dumb-init", "--"]
|
|
70
|
+
|
|
71
|
+
# Start the application
|
|
72
|
+
CMD ["node", "app.js"]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Variable Substitutions
|
|
78
|
+
|
|
79
|
+
Replace these placeholders with context values:
|
|
80
|
+
- `{port}` → `context.current_init.port` or `context.services[<service_name>].port`
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## File Location
|
|
85
|
+
|
|
86
|
+
Write to: `<service_name>/Dockerfile`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Dependencies
|
|
91
|
+
|
|
92
|
+
- Requires `package.json` to exist (Wave 1)
|
|
93
|
+
- Requires `app.js` to exist (Wave 5)
|
|
94
|
+
- Can be generated in Wave 6 (post-completion)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## What This Dockerfile Does
|
|
99
|
+
|
|
100
|
+
1. **Multi-stage build** — reduces final image size by 40-60%
|
|
101
|
+
2. **Alpine Linux** — minimal base image (~5MB vs ~150MB for full node)
|
|
102
|
+
3. **Non-root user** — security best practice
|
|
103
|
+
4. **dumb-init** — proper signal handling for graceful shutdowns
|
|
104
|
+
5. **Health check** — container orchestration support
|
|
105
|
+
6. **Production dependencies only** — no devDependencies in final image
|
|
106
|
+
7. **Layer caching** — package.json copied before app code for faster rebuilds
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Security Features
|
|
111
|
+
|
|
112
|
+
- Runs as non-root user (nodejs:nodejs with UID 1001)
|
|
113
|
+
- No unnecessary packages installed
|
|
114
|
+
- Proper file permissions for writable directories
|
|
115
|
+
- Health check endpoint at `/health` (assumes route exists)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Notes
|
|
120
|
+
|
|
121
|
+
- The health check assumes a `/health` route exists on the service.
|
|
122
|
+
If not present, the nodejs-agent should generate a basic health
|
|
123
|
+
route in the default module during initialization.
|
|
124
|
+
- Image size will be approximately 120-150MB depending on dependencies
|
|
125
|
+
- Build time is optimized through layer caching — changing app code
|
|
126
|
+
does not invalidate the npm install layer
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-dockerignore
|
|
4
|
+
agent: nodejs-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: .dockerignore
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Excludes unnecessary files and directories from the Docker build context,
|
|
11
|
+
reducing build time and final image size. Mirrors .gitignore but with
|
|
12
|
+
additional Docker-specific exclusions.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Template
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
# Dependencies
|
|
20
|
+
node_modules/
|
|
21
|
+
npm-debug.log
|
|
22
|
+
yarn-error.log
|
|
23
|
+
package-lock.json
|
|
24
|
+
|
|
25
|
+
# Environment files (will be provided at runtime)
|
|
26
|
+
.env
|
|
27
|
+
.env.*
|
|
28
|
+
!.env.example
|
|
29
|
+
|
|
30
|
+
# Logs
|
|
31
|
+
logger/logs/
|
|
32
|
+
*.log
|
|
33
|
+
|
|
34
|
+
# Development files
|
|
35
|
+
.vscode/
|
|
36
|
+
.idea/
|
|
37
|
+
.cursor/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
|
|
42
|
+
# Testing
|
|
43
|
+
coverage/
|
|
44
|
+
.nyc_output/
|
|
45
|
+
*.test.js
|
|
46
|
+
*.spec.js
|
|
47
|
+
__tests__/
|
|
48
|
+
|
|
49
|
+
# Documentation
|
|
50
|
+
README.md
|
|
51
|
+
docs/
|
|
52
|
+
*.md
|
|
53
|
+
!package.json
|
|
54
|
+
|
|
55
|
+
# Git
|
|
56
|
+
.git/
|
|
57
|
+
.gitignore
|
|
58
|
+
.gitattributes
|
|
59
|
+
|
|
60
|
+
# Docker
|
|
61
|
+
Dockerfile
|
|
62
|
+
.dockerignore
|
|
63
|
+
docker-compose*.yml
|
|
64
|
+
|
|
65
|
+
# OS files
|
|
66
|
+
.DS_Store
|
|
67
|
+
Thumbs.db
|
|
68
|
+
.cache/
|
|
69
|
+
|
|
70
|
+
# Build artifacts
|
|
71
|
+
dist/
|
|
72
|
+
build/
|
|
73
|
+
|
|
74
|
+
# IDE configs
|
|
75
|
+
.codeninja/
|
|
76
|
+
|
|
77
|
+
# Demo files
|
|
78
|
+
enc_dec.html
|
|
79
|
+
enc_dec.php
|
|
80
|
+
|
|
81
|
+
# Temporary files
|
|
82
|
+
tmp/
|
|
83
|
+
temp/
|
|
84
|
+
*.tmp
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## File Location
|
|
90
|
+
|
|
91
|
+
Write to: `<service_name>/.dockerignore`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Dependencies
|
|
96
|
+
|
|
97
|
+
None — can be generated in Wave 1 alongside .gitignore
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## What This File Does
|
|
102
|
+
|
|
103
|
+
1. **Excludes node_modules** — will be installed fresh in container
|
|
104
|
+
2. **Excludes .env** — environment variables passed at runtime
|
|
105
|
+
3. **Excludes logs** — logs are written at runtime, not baked into image
|
|
106
|
+
4. **Excludes development files** — IDE configs, test files, docs
|
|
107
|
+
5. **Excludes .codeninja** — agent system not needed in production
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Security Benefits
|
|
112
|
+
|
|
113
|
+
- `.env` files never accidentally copied into image
|
|
114
|
+
- Development-only secrets (.cursor, .vscode configs) excluded
|
|
115
|
+
- Smaller attack surface (fewer files in image)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Performance Benefits
|
|
120
|
+
|
|
121
|
+
- Faster builds (smaller context sent to Docker daemon)
|
|
122
|
+
- Better layer caching (changes to excluded files don't invalidate layers)
|
|
123
|
+
- Smaller final image size
|