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,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-headerValidator
|
|
4
|
+
agent: nodejs-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: middleware/headerValidator.js
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Central middleware file for all inbound request validation. Runs on every
|
|
11
|
+
request in sequence: language extraction → API key validation → token
|
|
12
|
+
validation. Also exposes request body decryption as a per-route middleware
|
|
13
|
+
when encrypted transport is enabled. All encryption/decryption operations
|
|
14
|
+
are delegated to `utilities/encryption.js`. All response sending is
|
|
15
|
+
delegated to `utilities/response.js`. All validation rule checking is
|
|
16
|
+
delegated to `utilities/validator.js`.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Dependencies to Import
|
|
21
|
+
|
|
22
|
+
- `../config/constants` — GLOBALS object containing API_KEY and other
|
|
23
|
+
app-wide constants. Note: ENCRYPTED_TRANSPORT is read from
|
|
24
|
+
process.env directly, not from constants.
|
|
25
|
+
- `../utilities/encryption` — imports `decrypt` function only.
|
|
26
|
+
The actual crypto library (crypto-js or cryptlib) is abstracted away.
|
|
27
|
+
headerValidator never calls crypto directly.
|
|
28
|
+
- `../utilities/response` — imports `sendResponse` function for all
|
|
29
|
+
outbound responses from this middleware.
|
|
30
|
+
- `../logger/logging` — imports `log` for info/debug/error logging.
|
|
31
|
+
- `localizify` — imported as `{ default: localizify }` and `{ t }`
|
|
32
|
+
separately in two destructured require statements.
|
|
33
|
+
- All language files from `../languages/` — imported individually, one
|
|
34
|
+
require per supported language. The list of supported languages comes
|
|
35
|
+
from `process.env.SUPPORTED_LANGUAGES` parsed as a comma-separated
|
|
36
|
+
array at module load time. Each language is imported as a variable
|
|
37
|
+
named by its code: e.g. `en` from `../languages/en.js`, `ar` from
|
|
38
|
+
`../languages/ar.js`. Agent generates one require line per language
|
|
39
|
+
in `context.services[<name>].supported_languages`.
|
|
40
|
+
- `jsonwebtoken` — for JWT verify operations in validateToken.
|
|
41
|
+
- `../utilities/ioRedis` — Redis client for token version lookup.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Module-Level Constants
|
|
46
|
+
|
|
47
|
+
### languageMap
|
|
48
|
+
Built once at module load time. A plain object that maps each language
|
|
49
|
+
code string to its imported language object.
|
|
50
|
+
Example structure: `{ "en": en, "ar": ar, "fr": fr }`
|
|
51
|
+
Keys are the language code strings from SUPPORTED_LANGUAGES parsed from
|
|
52
|
+
process.env. Values are the imported language file objects.
|
|
53
|
+
Used in `extractLanguage` for O(1) lookup by header value.
|
|
54
|
+
|
|
55
|
+
### DEFAULT_LANGUAGE
|
|
56
|
+
Read `process.env.DEFAULT_LANGUAGE` once at module load time.
|
|
57
|
+
Used as fallback in `extractLanguage` when the header is absent or
|
|
58
|
+
contains an unrecognized language code.
|
|
59
|
+
|
|
60
|
+
### bypassMethod
|
|
61
|
+
Array of route name strings that skip token validation entirely.
|
|
62
|
+
These are the final path segment of public endpoints — the segment at
|
|
63
|
+
index 2 when the path is split by `/`.
|
|
64
|
+
Default values to populate: `"login"`, `"signup"`, `"requestotp"`,
|
|
65
|
+
`"forgot-password"`, `"set-password"`.
|
|
66
|
+
Developer appends to this list as new public endpoints are added.
|
|
67
|
+
|
|
68
|
+
### bypassHeaderKey
|
|
69
|
+
Array of route name strings that skip API key validation.
|
|
70
|
+
Initialized as an empty array. Exists so future public or webhook routes
|
|
71
|
+
can be excluded without modifying validation logic. Developer adds
|
|
72
|
+
values here when needed.
|
|
73
|
+
|
|
74
|
+
### JWT_SECRET
|
|
75
|
+
Read `process.env.KEY` once at module load time.
|
|
76
|
+
Store as module-level constant `JWT_SECRET`.
|
|
77
|
+
This is the same KEY used for AES encryption throughout the service
|
|
78
|
+
and for JWT signing in `config/common.js` — they must always match.
|
|
79
|
+
Used by `jsonwebtoken.verify()` in validateToken.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Functions
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### extractLanguage(req, res, next)
|
|
88
|
+
|
|
89
|
+
**Purpose**: Determine the request language, register it with localizify,
|
|
90
|
+
and attach everything needed for translation onto the request object so
|
|
91
|
+
all downstream handlers work without any additional setup.
|
|
92
|
+
|
|
93
|
+
**Flow**:
|
|
94
|
+
|
|
95
|
+
1. Read the `accept-language` header from `req.headers`. Lowercase and
|
|
96
|
+
trim the value. If the header is absent or empty, assign
|
|
97
|
+
DEFAULT_LANGUAGE immediately and skip to step 3.
|
|
98
|
+
|
|
99
|
+
2. Check if the extracted value exists as a key in `languageMap`.
|
|
100
|
+
If it does not exist — the client sent an unsupported language code.
|
|
101
|
+
Fall back to DEFAULT_LANGUAGE silently. Language fallback must always
|
|
102
|
+
be transparent to the client. Never return an error for a bad language
|
|
103
|
+
header.
|
|
104
|
+
|
|
105
|
+
3. Using the resolved language code, look up the corresponding language
|
|
106
|
+
object from `languageMap`.
|
|
107
|
+
|
|
108
|
+
4. Register with localizify:
|
|
109
|
+
Call `localizify.add(resolvedCode, resolvedLanguageObject)` to
|
|
110
|
+
register the translations for this language in this request context.
|
|
111
|
+
Call `localizify.setLocale(resolvedCode)` to activate it.
|
|
112
|
+
Both calls must happen before any `t()` call in this request lifecycle.
|
|
113
|
+
|
|
114
|
+
Note on getMessage: `response.js` getMessage also calls
|
|
115
|
+
localizify.add and setLocale on every call, making it safe under
|
|
116
|
+
concurrent requests. The setLocale call here in extractLanguage is
|
|
117
|
+
for `req.t` — the function bound to the request object for use in
|
|
118
|
+
route handlers. These two patterns coexist correctly.
|
|
119
|
+
|
|
120
|
+
5. Attach to the request object:
|
|
121
|
+
- `req.lang` — the resolved language code string (e.g. `"en"`, `"ar"`).
|
|
122
|
+
Passed to `sendResponse` for message resolution on all subsequent
|
|
123
|
+
responses in this request.
|
|
124
|
+
- `req.languageData` — the full language object from `languageMap`.
|
|
125
|
+
Available to route handlers if they need direct key access without
|
|
126
|
+
going through localizify.
|
|
127
|
+
- `req.t` — the `t` function from localizify bound directly onto the
|
|
128
|
+
request. Route handlers call `req.t("keyword")` without importing
|
|
129
|
+
localizify themselves.
|
|
130
|
+
|
|
131
|
+
6. Log at info level: the resolved language code, whether fallback was
|
|
132
|
+
used (true/false), and the original header value.
|
|
133
|
+
|
|
134
|
+
7. Call `next()`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### validateApiKey(req, res, next)
|
|
139
|
+
|
|
140
|
+
**Purpose**: Verify that the inbound request carries a valid API key.
|
|
141
|
+
The client always sends the API key AES-encrypted. This middleware
|
|
142
|
+
decrypts it and compares against the expected value from constants.
|
|
143
|
+
|
|
144
|
+
**Flow**:
|
|
145
|
+
|
|
146
|
+
1. Split `req.path` by `/` and store as `pathSegments`. Extract
|
|
147
|
+
`pathSegments[2]` as the route identifier for bypass checking.
|
|
148
|
+
|
|
149
|
+
2. If `bypassHeaderKey` includes the route identifier — call `next()`
|
|
150
|
+
and return immediately. No further checks for this request.
|
|
151
|
+
|
|
152
|
+
3. Read the `api-key` header from `req.headers`. If the header is absent
|
|
153
|
+
or empty — send a 401 response using `sendResponse` with keyword
|
|
154
|
+
`rest_keywords_invalid_api_key` and response code `"-1"`. Log at
|
|
155
|
+
info level that the header was missing. Do not call `next()`.
|
|
156
|
+
|
|
157
|
+
4. Inside a try/catch block:
|
|
158
|
+
Call `decrypt(encryptedToken)` from `utilities/encryption.js`.
|
|
159
|
+
Strip all whitespace characters from the decrypted result.
|
|
160
|
+
Strip surrounding double-quote characters if present.
|
|
161
|
+
If the cleaned result is an empty string — call sendResponse with
|
|
162
|
+
401, keyword `rest_keywords_tokeninvalid`, response code `"-1"`.
|
|
163
|
+
Log at debug level: "Empty token after decryption".
|
|
164
|
+
Return immediately. Do not call next().
|
|
165
|
+
|
|
166
|
+
5. Verify the decrypted string as a JWT:
|
|
167
|
+
Call `jsonwebtoken.verify(token, JWT_SECRET, { algorithms: ['HS256'] })`.
|
|
168
|
+
JWT_SECRET is the module-level constant loaded at startup from
|
|
169
|
+
process.env.KEY. Algorithm must be HS256.
|
|
170
|
+
Wrap this call in the try/catch. If verification fails for any
|
|
171
|
+
reason (expired, tampered, wrong algorithm, malformed) — the catch
|
|
172
|
+
block handles the response. Do not throw manually.
|
|
173
|
+
On success, extract three fields from the decoded payload:
|
|
174
|
+
- `user_id` — numeric user identifier
|
|
175
|
+
- `user_type` — role or type string
|
|
176
|
+
- `version` — integer session version issued at login time
|
|
177
|
+
If any of these three fields is missing or undefined — call
|
|
178
|
+
sendResponse with 401, keyword `rest_keywords_tokeninvalid`,
|
|
179
|
+
response code `"-1"`. Log at debug level. Return immediately.
|
|
180
|
+
Do not call next().
|
|
181
|
+
|
|
182
|
+
6. If match — log success at info level with the path. Call `next()`.
|
|
183
|
+
|
|
184
|
+
7. In the catch block — any decryption failure (malformed cipher, wrong
|
|
185
|
+
key, encoding corruption) sends 401 with the same keyword. Log at
|
|
186
|
+
info level only — not error level — because this is an expected
|
|
187
|
+
attack surface. Never expose the decryption error detail to the
|
|
188
|
+
client.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### validateToken(req, res, next)
|
|
193
|
+
|
|
194
|
+
**Purpose**: Verify caller identity on protected routes using a JWT that
|
|
195
|
+
travels AES-encrypted in a custom header. Uses Redis for session version
|
|
196
|
+
checking to enforce logout invalidation without a database query on
|
|
197
|
+
every request.
|
|
198
|
+
|
|
199
|
+
**Flow**:
|
|
200
|
+
|
|
201
|
+
1. Split `req.path` by `/` and extract `pathSegments[2]` as the route
|
|
202
|
+
identifier.
|
|
203
|
+
|
|
204
|
+
2. If `bypassMethod` includes the route identifier — log the bypass at
|
|
205
|
+
info level, call `next()`, and return immediately. No token check
|
|
206
|
+
for public routes.
|
|
207
|
+
|
|
208
|
+
3. Read the `token` header from `req.headers`. If absent or empty —
|
|
209
|
+
send 401 with keyword `rest_keywords_tokeninvalid` and response code
|
|
210
|
+
`"-1"`. Do not call `next()`.
|
|
211
|
+
|
|
212
|
+
4. Inside a try/catch block:
|
|
213
|
+
Call `decrypt(encryptedToken)` from `utilities/encryption.js`.
|
|
214
|
+
Strip all whitespace characters from the decrypted result.
|
|
215
|
+
Strip surrounding double-quote characters if present.
|
|
216
|
+
If the cleaned result is an empty string — throw an error immediately
|
|
217
|
+
with a descriptive message such as "Empty token after decryption".
|
|
218
|
+
|
|
219
|
+
5. Verify the decrypted string as a JWT:
|
|
220
|
+
Call `jsonwebtoken.verify(token, JWT_SECRET, { algorithms: ['HS256'] })`.
|
|
221
|
+
JWT_SECRET is the module-level constant loaded at startup from
|
|
222
|
+
process.env.KEY — the same key used by common.generateSessionCode
|
|
223
|
+
to sign the token. Algorithm must be HS256 — the same algorithm
|
|
224
|
+
used at signing time.
|
|
225
|
+
If verification fails for any reason (expired, tampered, wrong
|
|
226
|
+
algorithm, malformed) — throw. Let the catch block handle the response.
|
|
227
|
+
On success, extract three fields from the decoded payload:
|
|
228
|
+
- `user_id` — numeric user identifier
|
|
229
|
+
- `user_type` — role or type string
|
|
230
|
+
- `version` — integer session version issued at login time
|
|
231
|
+
If any of these three fields is missing or undefined in the decoded
|
|
232
|
+
payload — throw immediately.
|
|
233
|
+
|
|
234
|
+
6. Query Redis using the ioRedis client from `utilities/ioRedis.js`:
|
|
235
|
+
Convert `user_id` to a string. Call `redis.get(user_id_string)`.
|
|
236
|
+
The stored value is a plain integer string — the current valid
|
|
237
|
+
session version for this user.
|
|
238
|
+
If the Redis key does not exist (returns null) — the user has no
|
|
239
|
+
active session. Send 401 with keyword `rest_keywords_tokeninvalid`.
|
|
240
|
+
Do not call `next()`.
|
|
241
|
+
|
|
242
|
+
7. Parse the Redis value as an integer. Call it `redisVersion`.
|
|
243
|
+
Compare with the `version` from the JWT payload:
|
|
244
|
+
- If JWT version is greater than or equal to redisVersion —
|
|
245
|
+
session is valid. Proceed.
|
|
246
|
+
- If JWT version is less than redisVersion — the session was
|
|
247
|
+
invalidated (user logged in elsewhere or was logged out).
|
|
248
|
+
Send 401 with keyword `rest_keywords_tokeninvalid`.
|
|
249
|
+
Log at info level: user_id, JWT version, Redis version.
|
|
250
|
+
Do not call `next()`.
|
|
251
|
+
|
|
252
|
+
8. On successful validation — attach to the request:
|
|
253
|
+
- `req.user_id` — the numeric user ID from the JWT payload
|
|
254
|
+
- `req.user_type` — the user type string from the JWT payload
|
|
255
|
+
Log at info level: token validated, user_id, user_type.
|
|
256
|
+
Call `next()`.
|
|
257
|
+
|
|
258
|
+
9. In the catch block — this handles only genuine exceptions from
|
|
259
|
+
jsonwebtoken.verify() (malformed token, wrong algorithm, expired).
|
|
260
|
+
Call sendResponse with 401, keyword `rest_keywords_tokeninvalid`,
|
|
261
|
+
response code `"-1"`. Log at debug level with the error message.
|
|
262
|
+
Never expose JWT internals or decryption details to the client.
|
|
263
|
+
All other failure conditions (empty token, missing payload fields,
|
|
264
|
+
Redis null, version mismatch) are handled inline above with direct
|
|
265
|
+
sendResponse calls and early returns — they never reach this catch.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### decryptRequest(req, res, next)
|
|
270
|
+
|
|
271
|
+
**Generation rule**:
|
|
272
|
+
Read `context.services[<name>].encrypted_transport`.
|
|
273
|
+
- If `false` — do not generate this function at all. It will not exist
|
|
274
|
+
in the file and will not be exported. No reference to it anywhere.
|
|
275
|
+
- If `true` — generate this function as described below.
|
|
276
|
+
|
|
277
|
+
**Purpose**: Global middleware that decrypts the incoming request body
|
|
278
|
+
before any route handler runs. Applied in route_manager.js as part of
|
|
279
|
+
the global middleware chain — not per-route. Only generated when
|
|
280
|
+
`encrypted_transport == true`.
|
|
281
|
+
|
|
282
|
+
**Flow**:
|
|
283
|
+
|
|
284
|
+
1. Check the HTTP method via `req.method`. If the method is `"GET"` or
|
|
285
|
+
`"DELETE"` — call `next()` immediately and return. These methods
|
|
286
|
+
carry no body by convention. Decrypting an empty body causes
|
|
287
|
+
unnecessary errors.
|
|
288
|
+
|
|
289
|
+
2. Check `req.originalUrl` for demo route patterns. If the URL contains
|
|
290
|
+
`/decryption_demo` or `/encryption_demo` — call `next()` immediately.
|
|
291
|
+
These routes are used for testing and receive raw unencrypted bodies.
|
|
292
|
+
|
|
293
|
+
3. If `req.body` is absent, null, undefined, or an empty string — set
|
|
294
|
+
`req.body` to an empty object `{}` and call `next()` without
|
|
295
|
+
attempting decryption.
|
|
296
|
+
|
|
297
|
+
4. Inside a try/catch — call `decrypt(req.body)` from
|
|
298
|
+
`utilities/encryption.js`. Replace `req.body` entirely with the
|
|
299
|
+
decrypted and JSON-parsed result.
|
|
300
|
+
|
|
301
|
+
5. If `req.body.user_id` is undefined after decryption — inject it from
|
|
302
|
+
`req.user_id` if that value was set by `validateToken`, otherwise
|
|
303
|
+
set to `0`. This ensures every downstream model function receives
|
|
304
|
+
`req.body.user_id` without needing to check two sources.
|
|
305
|
+
|
|
306
|
+
6. Log at info level: decryption successful, request path.
|
|
307
|
+
Call `next()`.
|
|
308
|
+
|
|
309
|
+
7. In the catch block — log at error level with the full error. Send a
|
|
310
|
+
500 response with keyword `rest_keywords_decryption_failure` using
|
|
311
|
+
`sendResponse`.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Redis Session Version Contract
|
|
316
|
+
|
|
317
|
+
This is the source of truth for the login/logout version system. All
|
|
318
|
+
agents that generate login, logout, and session management logic must
|
|
319
|
+
follow this contract exactly.
|
|
320
|
+
|
|
321
|
+
- On every successful login:
|
|
322
|
+
Read current version from Redis for this user_id.
|
|
323
|
+
If the key exists — increment the stored value by 1.
|
|
324
|
+
If the key does not exist — set it to 1.
|
|
325
|
+
Store using `redis.set(user_id_string, newVersion)` with no expiry.
|
|
326
|
+
Issue the JWT with `version: newVersion` in the payload.
|
|
327
|
+
|
|
328
|
+
- On logout:
|
|
329
|
+
Increment the Redis version by 1. Do not delete the key.
|
|
330
|
+
All existing JWTs for this user become stale immediately because their
|
|
331
|
+
version value will now be less than the Redis value.
|
|
332
|
+
|
|
333
|
+
- On validateToken:
|
|
334
|
+
JWT version must be greater than or equal to Redis version.
|
|
335
|
+
Less than means logged out or superseded by a newer login —
|
|
336
|
+
treat as invalid.
|
|
337
|
+
|
|
338
|
+
- On force-logout all devices:
|
|
339
|
+
Set the Redis value to any number greater than the highest issued JWT
|
|
340
|
+
version for this user. All tokens across all devices become invalid
|
|
341
|
+
simultaneously without touching the database.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Export
|
|
346
|
+
|
|
347
|
+
Export as a plain object:
|
|
348
|
+
`module.exports = headerValidator`
|
|
349
|
+
|
|
350
|
+
Exported keys when `encrypted_transport == true`:
|
|
351
|
+
`extractLanguage`, `validateApiKey`, `validateToken`, `decryptRequest`
|
|
352
|
+
|
|
353
|
+
Exported keys when `encrypted_transport == false`:
|
|
354
|
+
`extractLanguage`, `validateApiKey`, `validateToken`
|
|
355
|
+
|
|
356
|
+
Do not export any encryption functions, response functions, getMessage,
|
|
357
|
+
or checkValidationRules from this file — those live in their own utility
|
|
358
|
+
files.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## What This File Does NOT Do
|
|
363
|
+
|
|
364
|
+
- Does not call crypto-js or cryptlib directly — all crypto goes through
|
|
365
|
+
`utilities/encryption.js`
|
|
366
|
+
- Does not call `res.json()` or `res.status()` directly — all responses
|
|
367
|
+
go through `utilities/response.js`
|
|
368
|
+
- Does not perform field-level request validation — that is in
|
|
369
|
+
`utilities/validator.js`
|
|
370
|
+
- Does not connect to the database — token validation uses Redis only
|
|
371
|
+
- Does not use RSA PEM keys — JWT uses HMAC-SHA256 (HS256) with
|
|
372
|
+
process.env.KEY, consistent with how tokens are signed in
|
|
373
|
+
config/common.js generateSessionCode
|
|
374
|
+
- Does not define getMessage, sendResponse, encrypt, or
|
|
375
|
+
checkValidationRules — those live in their own utility files
|
|
376
|
+
- Does not generate decryptRequest when `encrypted_transport == false` —
|
|
377
|
+
the function simply does not exist in the file in that case
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-ide-configs
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Task: Generate IDE Configuration Files
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
Confirms IDE configuration status when @initialize-project runs.
|
|
10
|
+
|
|
11
|
+
All IDE config files are written automatically during `codeninja init`
|
|
12
|
+
(the npm install step), NOT during this task. This task exists only to
|
|
13
|
+
surface the confirmation to the user so they know their IDE is connected.
|
|
14
|
+
|
|
15
|
+
If for any reason the config files are missing (e.g. the developer
|
|
16
|
+
copied .codeninja manually without running init), this task re-creates them.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## When to Run
|
|
21
|
+
|
|
22
|
+
Called from `initialize-project.workflow.md` at step 23b.
|
|
23
|
+
Only runs on the FIRST @initialize-project in this repo.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## What to Check and Report
|
|
28
|
+
|
|
29
|
+
### File 1 — .vscode/mcp.json
|
|
30
|
+
Check if `.vscode/mcp.json` exists.
|
|
31
|
+
- If YES → report ✓ already configured
|
|
32
|
+
- If NO → create it:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"servers": {
|
|
37
|
+
"codeninja": {
|
|
38
|
+
"type": "stdio",
|
|
39
|
+
"command": "node",
|
|
40
|
+
"args": ["${workspaceFolder}/.codeninja/mcp-server.js"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Note: VS Code uses `"servers"` not `"mcpServers"`.
|
|
47
|
+
Note: `${workspaceFolder}` is a VS Code variable — write it literally.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### File 2 — .cursor/mcp.json
|
|
52
|
+
Check if `.cursor/mcp.json` exists.
|
|
53
|
+
- If YES → report ✓ already configured
|
|
54
|
+
- If NO → create it:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"codeninja": {
|
|
60
|
+
"command": "node",
|
|
61
|
+
"args": [".codeninja/mcp-server.js"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### File 3 — Antigravity (~/.gemini/antigravity/mcp_config.json)
|
|
70
|
+
Antigravity uses a GLOBAL config file outside the project.
|
|
71
|
+
Check if the codeninja entry exists in it.
|
|
72
|
+
- If YES → report ✓ already configured
|
|
73
|
+
- If NO → inform the user it should have been written by `codeninja init`
|
|
74
|
+
and show them the manual fallback:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
─── Antigravity (manual fallback) ───────────────────────
|
|
78
|
+
Config file: ~/.gemini/antigravity/mcp_config.json
|
|
79
|
+
(Windows: %USERPROFILE%\.gemini\antigravity\mcp_config.json)
|
|
80
|
+
|
|
81
|
+
Add inside "mcpServers":
|
|
82
|
+
|
|
83
|
+
"codeninja": {
|
|
84
|
+
"command": "node",
|
|
85
|
+
"args": ["/ABSOLUTE/PATH/TO/PROJECT/.codeninja/mcp-server.js"]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Then go to Antigravity → ... → MCP Servers → Manage MCP Servers → Refresh.
|
|
89
|
+
─────────────────────────────────────────────────────────
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### Claude Desktop — Print Only
|
|
95
|
+
Claude Desktop config lives outside the project.
|
|
96
|
+
Always print this reminder block regardless of whether it was already set up.
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
─── Claude Desktop (manual step — one time only) ──────
|
|
100
|
+
Config file:
|
|
101
|
+
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
102
|
+
Windows: %APPDATA%\Claude\claude_desktop_config.json
|
|
103
|
+
Linux: ~/.config/claude/claude_desktop_config.json
|
|
104
|
+
|
|
105
|
+
Add inside "mcpServers":
|
|
106
|
+
|
|
107
|
+
"codeninja": {
|
|
108
|
+
"command": "node",
|
|
109
|
+
"args": ["/ABSOLUTE/PATH/TO/PROJECT/.codeninja/mcp-server.js"]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Save and restart Claude Desktop.
|
|
113
|
+
─────────────────────────────────────────────────────────
|
|
114
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
name: generate-ioRedis
|
|
4
|
+
agent: nodejs-agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# File: utilities/ioRedis.js
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
Creates, configures, and exports a single ioredis connection instance.
|
|
11
|
+
All files that need Redis (headerValidator for token version checking,
|
|
12
|
+
model files for caching) import this single shared connection. The
|
|
13
|
+
connection is established once at module load time.
|
|
14
|
+
|
|
15
|
+
This task also defines the Redis installation and startup commands that
|
|
16
|
+
the agent outputs to the developer as part of the `@initialize-project`
|
|
17
|
+
final summary, so the developer can get Redis running locally before
|
|
18
|
+
starting the service.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Dependencies to Import
|
|
23
|
+
|
|
24
|
+
- `ioredis` — imported as `IORedis` (capital letters). The Redis client
|
|
25
|
+
library.
|
|
26
|
+
- `../logger/logging` — imported as `log`. Used for connection success
|
|
27
|
+
and failure logging.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Module-Level Variables
|
|
32
|
+
|
|
33
|
+
### connection
|
|
34
|
+
Declared with `let` before the try/catch block so it is in scope for
|
|
35
|
+
the export line. Initialized inside the try block.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Connection Setup
|
|
40
|
+
|
|
41
|
+
Wrap the entire connection creation in a try/catch block.
|
|
42
|
+
|
|
43
|
+
Inside try:
|
|
44
|
+
Create `new IORedis` instance with these configuration values:
|
|
45
|
+
- `host` — read from `process.env.REDIS_HOST`. This value is already
|
|
46
|
+
in `.env` from the initialize-project workflow.
|
|
47
|
+
- `port` — read from `process.env.REDIS_PORT` parsed as an integer.
|
|
48
|
+
- `maxRetriesPerRequest: null` — required when using ioredis with
|
|
49
|
+
BullMQ or any queue library. Also prevents ioredis from throwing
|
|
50
|
+
on commands during reconnection. Set this always regardless of
|
|
51
|
+
whether queues are used — it is a safe default.
|
|
52
|
+
|
|
53
|
+
After successful instantiation — log at info level with message
|
|
54
|
+
"Successfully connected to Redis" and include host and port values.
|
|
55
|
+
|
|
56
|
+
Inside catch:
|
|
57
|
+
Log at error level with message "Failed to connect to Redis" and the
|
|
58
|
+
error object. Re-throw the error. A Redis connection failure at startup
|
|
59
|
+
is unrecoverable — the service should not start without Redis since
|
|
60
|
+
token validation depends on it.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Redis Setup Instructions (output by agent in final summary)
|
|
65
|
+
|
|
66
|
+
When this file is generated, the agent appends the following Redis
|
|
67
|
+
setup instructions to the `@initialize-project` final summary output.
|
|
68
|
+
These are not written into the file itself — they are printed to the
|
|
69
|
+
developer as next steps.
|
|
70
|
+
|
|
71
|
+
### macOS (Homebrew)
|
|
72
|
+
```
|
|
73
|
+
brew install redis
|
|
74
|
+
brew services start redis
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Ubuntu / Debian Linux
|
|
78
|
+
```
|
|
79
|
+
sudo apt update
|
|
80
|
+
sudo apt install redis-server
|
|
81
|
+
sudo systemctl start redis
|
|
82
|
+
sudo systemctl enable redis
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Windows
|
|
86
|
+
```
|
|
87
|
+
winget install Redis.Redis
|
|
88
|
+
redis-server
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Verify Redis is running (all platforms)
|
|
92
|
+
```
|
|
93
|
+
redis-cli ping
|
|
94
|
+
```
|
|
95
|
+
Expected response: `PONG`
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Export
|
|
100
|
+
```
|
|
101
|
+
module.exports = connection
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The connection instance is exported directly. Imported as:
|
|
105
|
+
`const redis = require('../utilities/ioRedis')`
|
|
106
|
+
|
|
107
|
+
Then used as: `redis.get(key)`, `redis.set(key, value)`, etc.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What This File Does NOT Do
|
|
112
|
+
|
|
113
|
+
- Does not define any Redis helper functions — raw ioredis methods are
|
|
114
|
+
used directly by callers
|
|
115
|
+
- Does not implement retry logic beyond ioredis built-in reconnection
|
|
116
|
+
- Does not expire keys — TTL decisions belong to the caller
|
|
117
|
+
- Does not implement pub/sub — that would be a separate connection
|
|
118
|
+
instance if needed
|
|
119
|
+
- Does not fall back to in-memory storage if Redis is unavailable —
|
|
120
|
+
startup failure is the correct behavior
|