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.
Files changed (140) hide show
  1. package/.gitattributes +11 -0
  2. package/README.md +293 -0
  3. package/agent/database-agent.md +504 -0
  4. package/agent/designs/README.md +10 -0
  5. package/agent/global-agent.md +236 -0
  6. package/agent/nodejs-agent.md +406 -0
  7. package/agent/reactjs-agent.md +260 -0
  8. package/cli.js +352 -0
  9. package/commands/audit.workflow.md +111 -0
  10. package/commands/create-api.workflow.md +99 -0
  11. package/commands/db-add-index.workflow.md +97 -0
  12. package/commands/db-create-table.workflow.md +132 -0
  13. package/commands/db-drop-table.workflow.md +103 -0
  14. package/commands/db-modify-table.workflow.md +159 -0
  15. package/commands/db-seed.workflow.md +99 -0
  16. package/commands/db-sync.workflow.md +100 -0
  17. package/commands/design.workflow.md +66 -0
  18. package/commands/initialize-project.workflow.md +500 -0
  19. package/commands/integrate-api.workflow.md +448 -0
  20. package/commands/modularize.workflow.md +329 -0
  21. package/commands/refactor.workflow.md +70 -0
  22. package/commands/sync.workflow.md +962 -0
  23. package/commands/test.workflow.md +40 -0
  24. package/commands/validate-page.workflow.md +543 -0
  25. package/mcp-server.js +842 -0
  26. package/package.json +24 -0
  27. package/tasks/README.md +283 -0
  28. package/tasks/add-health-route.task.md +103 -0
  29. package/tasks/ask-api-integration-scope.task.md +34 -0
  30. package/tasks/ask-api-key.task.md +23 -0
  31. package/tasks/ask-api-version.task.md +28 -0
  32. package/tasks/ask-client-type.task.md +24 -0
  33. package/tasks/ask-column-enum-values.task.md +51 -0
  34. package/tasks/ask-column-is-enum.task.md +39 -0
  35. package/tasks/ask-column-name.task.md +39 -0
  36. package/tasks/ask-column-position.task.md +39 -0
  37. package/tasks/ask-column-type.task.md +59 -0
  38. package/tasks/ask-database-config.task.md +66 -0
  39. package/tasks/ask-database-host.task.md +16 -0
  40. package/tasks/ask-database-name.task.md +18 -0
  41. package/tasks/ask-database-port.task.md +23 -0
  42. package/tasks/ask-database-type.task.md +30 -0
  43. package/tasks/ask-database-user.task.md +14 -0
  44. package/tasks/ask-design-description.task.md +16 -0
  45. package/tasks/ask-design-target.task.md +24 -0
  46. package/tasks/ask-encrypted-transport.task.md +25 -0
  47. package/tasks/ask-encryption-iv.task.md +23 -0
  48. package/tasks/ask-encryption-key.task.md +23 -0
  49. package/tasks/ask-feature-name.task.md +20 -0
  50. package/tasks/ask-http-method.task.md +21 -0
  51. package/tasks/ask-index-columns.task.md +46 -0
  52. package/tasks/ask-index-file-placement.task.md +33 -0
  53. package/tasks/ask-index-sort-order.task.md +37 -0
  54. package/tasks/ask-index-type.task.md +42 -0
  55. package/tasks/ask-init-mode.task.md +28 -0
  56. package/tasks/ask-linked-service.task.md +57 -0
  57. package/tasks/ask-modify-operation.task.md +36 -0
  58. package/tasks/ask-modularize-scope.task.md +31 -0
  59. package/tasks/ask-module-name.task.md +30 -0
  60. package/tasks/ask-new-column-name.task.md +21 -0
  61. package/tasks/ask-new-table-name.task.md +22 -0
  62. package/tasks/ask-old-column-name.task.md +22 -0
  63. package/tasks/ask-package-author.task.md +16 -0
  64. package/tasks/ask-package-name.task.md +23 -0
  65. package/tasks/ask-page-path.task.md +40 -0
  66. package/tasks/ask-primary-table.task.md +30 -0
  67. package/tasks/ask-project-figma.task.md +71 -0
  68. package/tasks/ask-project-info-doc.task.md +57 -0
  69. package/tasks/ask-project-scope-of-work.task.md +57 -0
  70. package/tasks/ask-project-type.task.md +24 -0
  71. package/tasks/ask-react-target-service.task.md +32 -0
  72. package/tasks/ask-redis-config.task.md +42 -0
  73. package/tasks/ask-redis-host.task.md +16 -0
  74. package/tasks/ask-redis-port.task.md +18 -0
  75. package/tasks/ask-refactor-type.task.md +26 -0
  76. package/tasks/ask-requires-auth.task.md +22 -0
  77. package/tasks/ask-response-mode.task.md +38 -0
  78. package/tasks/ask-route-description.task.md +20 -0
  79. package/tasks/ask-route-path.task.md +29 -0
  80. package/tasks/ask-seed-row-values.task.md +42 -0
  81. package/tasks/ask-seed-rows-count.task.md +22 -0
  82. package/tasks/ask-service-description.task.md +16 -0
  83. package/tasks/ask-service-name.task.md +27 -0
  84. package/tasks/ask-service-port.task.md +24 -0
  85. package/tasks/ask-supported-languages.task.md +40 -0
  86. package/tasks/ask-table-file-number.task.md +36 -0
  87. package/tasks/ask-table-indexes.task.md +47 -0
  88. package/tasks/ask-table-name.task.md +32 -0
  89. package/tasks/ask-table-needs-soft-delete.task.md +29 -0
  90. package/tasks/ask-table-needs-status.task.md +30 -0
  91. package/tasks/ask-table-purpose.task.md +28 -0
  92. package/tasks/ask-table-seed-data.task.md +44 -0
  93. package/tasks/ask-target-service.task.md +32 -0
  94. package/tasks/ask-test-type.task.md +20 -0
  95. package/tasks/ask-validation-library.task.md +38 -0
  96. package/tasks/detect-repository-state.task.md +92 -0
  97. package/tasks/generate-app.task.md +146 -0
  98. package/tasks/generate-common.task.md +330 -0
  99. package/tasks/generate-constants.task.md +123 -0
  100. package/tasks/generate-database.task.md +168 -0
  101. package/tasks/generate-docker-compose.task.md +298 -0
  102. package/tasks/generate-dockerfile.task.md +126 -0
  103. package/tasks/generate-dockerignore.task.md +123 -0
  104. package/tasks/generate-enc-dec-html.task.md +127 -0
  105. package/tasks/generate-enc-dec-php.task.md +145 -0
  106. package/tasks/generate-encryption.task.md +159 -0
  107. package/tasks/generate-fast-defaults.task.md +68 -0
  108. package/tasks/generate-gitignore.task.md +79 -0
  109. package/tasks/generate-headerValidator.task.md +377 -0
  110. package/tasks/generate-ide-configs.task.md +114 -0
  111. package/tasks/generate-ioRedis.task.md +120 -0
  112. package/tasks/generate-language-en.task.md +155 -0
  113. package/tasks/generate-logging.task.md +257 -0
  114. package/tasks/generate-model.task.md +180 -0
  115. package/tasks/generate-notification.task.md +251 -0
  116. package/tasks/generate-package-json.task.md +114 -0
  117. package/tasks/generate-rateLimiter.task.md +125 -0
  118. package/tasks/generate-react-api-client.task.md +169 -0
  119. package/tasks/generate-react-api-handler.task.md +102 -0
  120. package/tasks/generate-react-app-jsx.task.md +56 -0
  121. package/tasks/generate-react-dockerfile.task.md +175 -0
  122. package/tasks/generate-react-env.task.md +58 -0
  123. package/tasks/generate-react-gitignore.task.md +49 -0
  124. package/tasks/generate-react-htaccess.task.md +54 -0
  125. package/tasks/generate-react-index-html.task.md +53 -0
  126. package/tasks/generate-react-index-jsx.task.md +51 -0
  127. package/tasks/generate-react-package-json.task.md +77 -0
  128. package/tasks/generate-react-welcome-page.task.md +71 -0
  129. package/tasks/generate-readme.task.md +160 -0
  130. package/tasks/generate-response.task.md +202 -0
  131. package/tasks/generate-route-manager.task.md +173 -0
  132. package/tasks/generate-route.task.md +203 -0
  133. package/tasks/generate-swagger.task.md +290 -0
  134. package/tasks/generate-tbl-user-deviceinfo.task.md +75 -0
  135. package/tasks/generate-template.task.md +129 -0
  136. package/tasks/generate-validator.task.md +122 -0
  137. package/tasks/show-db-table-summary.task.md +66 -0
  138. package/tasks/show-final-summary.task.md +108 -0
  139. package/tasks/show-init-summary.task.md +257 -0
  140. package/tasks/write-context.task.md +314 -0
@@ -0,0 +1,146 @@
1
+ ---
2
+ type: task
3
+ name: generate-app
4
+ agent: nodejs-agent
5
+ ---
6
+
7
+ # File: app.js
8
+
9
+ ## Purpose
10
+ Express application entry point. Loads environment variables, configures
11
+ all middleware in the correct order, registers versioned routes, and starts
12
+ the HTTP server. This is the first file Node.js executes when the service
13
+ starts.
14
+
15
+ ---
16
+
17
+ ## Dependencies to Import
18
+
19
+ - `dotenv` — called as `require('dotenv').config()` on the very first
20
+ line before any other import. This ensures all process.env values are
21
+ available to every module that loads after it.
22
+ - `express` — the web framework.
23
+ - `cors` — only imported if `client_type == "reactjs"`. Mobile/app
24
+ clients do not use browser CORS. If `client_type == "app"` — do not
25
+ import or use cors at all.
26
+ - `./logger/logging` — imported as `log`. Used for startup success and
27
+ startup failure messages only.
28
+ - `./modules/v1/route_manager` — the versioned router that mounts all
29
+ module routes and runs the middleware chain.
30
+
31
+ No other imports. app.js does not import headerValidator, encryption,
32
+ response, or any module directly — those are all handled inside
33
+ route_manager.
34
+
35
+ ---
36
+
37
+ ## CORS Configuration (client_type == "reactjs" only)
38
+
39
+ When `client_type == "reactjs"`, CORS must be configured with an allowed
40
+ origins list read from environment variables.
41
+
42
+ The allowed origins list is built from `process.env.ALLOWED_ORIGINS`.
43
+ This value is a comma-separated string in `.env`. Parse it by splitting
44
+ on commas and trimming each entry. Store as an array.
45
+
46
+ Always include `http://localhost:3000` and `http://127.0.0.1:3000` in
47
+ the allowed origins list in addition to whatever is in the environment
48
+ variable. These are development defaults and must always be present.
49
+
50
+ The CORS configuration function receives `origin` and `callback`:
51
+ - If `origin` is undefined or null — allow the request. This covers
52
+ server-to-server calls and tools like Postman/curl that send no Origin
53
+ header.
54
+ - If `origin` is in the allowed origins array — call `callback(null, true)`.
55
+ - If `origin` is not in the list — call `callback(new Error('Not allowed
56
+ by CORS'))`. Do not send a response here — Express handles the error.
57
+
58
+ Set `credentials: true` in the CORS options. This allows the client to
59
+ send cookies and authorization headers in cross-origin requests.
60
+
61
+ When `client_type == "app"` — skip CORS entirely. No import, no
62
+ configuration, no `app.use(cors(...))` line.
63
+
64
+ ---
65
+
66
+ ## Body Parser Configuration
67
+
68
+ The body parser setup depends on `encrypted_transport`:
69
+
70
+ If `encrypted_transport == true`:
71
+ The entire request body is an encrypted cipher string — it arrives as
72
+ plain text, not as a JSON object. Express must be configured to parse
73
+ it as text first so the decryption middleware receives a string.
74
+ Apply in this order:
75
+ 1. `express.text()` — parses body as a raw string regardless of
76
+ Content-Type. This catches the encrypted payload.
77
+ 2. `express.json()` — still included as fallback for any internal or
78
+ tooling requests that send JSON directly.
79
+ 3. `express.urlencoded({ extended: false })` — for form submissions.
80
+
81
+ If `encrypted_transport == false`:
82
+ Request bodies are plain JSON. Only apply:
83
+ 1. `express.json()`
84
+ 2. `express.urlencoded({ extended: false })`
85
+ Do not apply `express.text()` — it would interfere with JSON parsing.
86
+
87
+ ---
88
+
89
+ ## Middleware and Route Registration Order
90
+
91
+ Apply middleware and routes in this exact sequence:
92
+
93
+ 1. Body parsers (as described above based on encrypted_transport)
94
+ 2. CORS (if client_type == "reactjs")
95
+ 3. Route registration: `app.use('/api/v1', route_manager)`
96
+
97
+ The headerValidator middleware chain (extractLanguage, validateApiKey,
98
+ validateToken, decryptRequest) is NOT applied in app.js. It is applied
99
+ inside route_manager.js. app.js only applies global infrastructure
100
+ middleware — body parsing and CORS.
101
+
102
+ ---
103
+
104
+ ## Server Startup
105
+
106
+ Define an async function called `launchApp`. This is the only async
107
+ function in the file.
108
+
109
+ Inside `launchApp`:
110
+ 1. Read the port from `process.env.PORT`. If not set, use a fallback
111
+ default port from `process.env.PROJECT_NAME` context — agent reads
112
+ `context.services[<name>].port` and uses it as the hardcoded fallback.
113
+ Never use an arbitrary number like 3000 or 7001 — always use the
114
+ port from context.
115
+ 2. Call `app.listen(port, callback)`. In the callback — call
116
+ `log.info` with the service name and port number. Use
117
+ `process.env.PROJECT_NAME` for the service name in the log message.
118
+ 3. Wrap in try/catch. On any error — call `log.critical` with the
119
+ service name and error object, then call `process.exit(1)`.
120
+
121
+ Call `launchApp()` at the bottom of the file. No export — this file
122
+ is never required by another file.
123
+
124
+ ---
125
+
126
+ ## .env additions required by this file
127
+
128
+ Add to the `.env` template in nodejs-agent:
129
+ ```
130
+ ALLOWED_ORIGINS=https://yourfrontend.com,https://yourstaging.com
131
+ ```
132
+ This key is only relevant when `client_type == "reactjs"`. When
133
+ `client_type == "app"` this line is omitted from `.env`.
134
+
135
+ ---
136
+
137
+ ## What This File Does NOT Do
138
+
139
+ - Does not import or configure helmet — security headers are not applied
140
+ at this level in this codebase
141
+ - Does not define any routes directly — all routes go through route_manager
142
+ - Does not import headerValidator — middleware chain is route_manager's
143
+ responsibility
144
+ - Does not handle errors globally with `app.use((err, req, res, next)=>{})`
145
+ — errors propagate through the asyncHandler wrapper in route_manager
146
+ - Does not export `app` — this file is the entry point, not a module
@@ -0,0 +1,330 @@
1
+ ---
2
+ type: task
3
+ name: generate-common
4
+ agent: nodejs-agent
5
+ ---
6
+
7
+ # File: config/common.js
8
+
9
+ ## Purpose
10
+ Shared utility object containing functions used across the entire service.
11
+ Imported by model files, notification.js, and any file that needs device
12
+ info management, generic DB operations, session generation, or OTP
13
+ generation. All database-touching functions use the query interface from
14
+ config/database.js. The object is named `common` and exported directly.
15
+
16
+ ---
17
+
18
+ ## Dependencies to Import
19
+
20
+ - `./database` — imported as `con`. Used by all database functions.
21
+ - `./constants` — imported as `GLOBALS`. Available for any function
22
+ that needs app-wide constants.
23
+ - `pg-format` — imported as `format`. Used to safely build dynamic SQL
24
+ queries with variable column names and values. Never used for query
25
+ execution — only for building query strings that are then passed to
26
+ `con.query`.
27
+ - `../logger/logging` — imported as `log`. Used for error and info
28
+ logging in all functions.
29
+ - `jsonwebtoken` — imported as `jwt`. Used in `generateSessionCode` for
30
+ JWT signing.
31
+ - `../utilities/ioRedis` — imported as `redis`. Used in
32
+ `generateSessionCode` to store the session version.
33
+ - `moment` — imported as `moment`. Available for date formatting in
34
+ utility functions.
35
+
36
+ ---
37
+
38
+ ## The common Object
39
+
40
+ All functions are methods of a single plain object named `common`.
41
+ Not a class — a plain object literal. All methods are async unless
42
+ explicitly noted as synchronous.
43
+
44
+ ---
45
+
46
+ ## Functions
47
+
48
+ ---
49
+
50
+ ### async generateSessionCode(user_id, user_type)
51
+
52
+ **Purpose**: Generates a signed JWT token for a user session, persists
53
+ device info with version to the database, and stores the version in
54
+ Redis. This is the single function called by login and signup model
55
+ functions to create a new session. Returns the signed JWT string.
56
+
57
+ **Parameters**:
58
+ - `user_id` — numeric user identifier
59
+ - `user_type` — string indicating the user role or type
60
+
61
+ **Flow**:
62
+
63
+ 1. Determine the new session version:
64
+ Query Redis using `redis.get(user_id.toString())`.
65
+ If a value exists — parse it as integer and add 1. This is the new
66
+ version.
67
+ If no value exists (first login ever) — version is 1.
68
+ Store the new version in the string `newVersion`.
69
+
70
+ 2. Build the JWT payload object with exactly three keys:
71
+ - `user_id` — the user_id parameter
72
+ - `user_type` — the user_type parameter
73
+ - `version` — newVersion integer
74
+
75
+ 3. Sign the JWT:
76
+ Call `jwt.sign(payload, process.env.KEY, { algorithm: 'HS256' })`.
77
+ Note: this uses HMAC-SHA256 with the KEY from .env as the secret —
78
+ not RSA PEM keys. No expiration is set — session invalidation is
79
+ handled entirely by the Redis version mechanism.
80
+ Store the signed token string as `jwtToken`.
81
+
82
+ 4. Store the new version in Redis:
83
+ Call `redis.set(user_id.toString(), newVersion.toString())`.
84
+ No expiry. The Redis key persists until logout increments the version.
85
+
86
+ 5. Check if device info already exists for this user:
87
+ Call `await this.checkDeviceInfo(user_id, user_type)`.
88
+ Store result as `deviceinfo`.
89
+
90
+ 6. If `deviceinfo` is not null — a record already exists:
91
+ Call `await this.updateDeviceInfo(user_id, user_type, { token: jwtToken, version: newVersion })`.
92
+
93
+ 7. If `deviceinfo` is null — no record exists yet:
94
+ Call `await this.addDeviceInformation({ token: jwtToken, user_id, user_type, version: newVersion })`.
95
+
96
+ 8. Return `jwtToken`.
97
+
98
+ ---
99
+
100
+ ### async checkDeviceInfo(user_id, user_type)
101
+
102
+ **Purpose**: Checks whether a device info record exists for the given
103
+ user_id and user_type combination in `tbl_user_deviceinfo`.
104
+
105
+ **Parameters**:
106
+ - `user_id` — numeric user identifier
107
+ - `user_type` — string role/type
108
+
109
+ **Flow**:
110
+
111
+ 1. Build parameterized query selecting all columns from
112
+ `tbl_user_deviceinfo` where `user_id = $1 AND user_type = $2`.
113
+ Values array: `[user_id, user_type]`.
114
+
115
+ 2. Call `con.query(queryText, values)` inside a try/catch.
116
+
117
+ 3. On success:
118
+ If `result.rowCount > 0` — return `result.rows[0]` (the first row
119
+ object).
120
+ If `result.rowCount === 0` — return `null`.
121
+
122
+ 4. On error — log at error level with function name and error. Return
123
+ `null`. Never throw.
124
+
125
+ ---
126
+
127
+ ### async updateDeviceInfo(user_id, user_type, params)
128
+
129
+ **Purpose**: Updates the device info record for the given user_id and
130
+ user_type with the provided field values.
131
+
132
+ **Parameters**:
133
+ - `user_id` — numeric user identifier
134
+ - `user_type` — string role/type
135
+ - `params` — plain object where keys are column names and values are
136
+ the new values to set
137
+
138
+ **Flow**:
139
+
140
+ 1. Build the SET clause dynamically using `pg-format`:
141
+ Iterate `Object.entries(params)`, format each pair as
142
+ `format("%I = %L", key, val)`, join with `, `.
143
+
144
+ 2. Build the full UPDATE query using `format`:
145
+ `UPDATE tbl_user_deviceinfo SET <setClause> WHERE user_id = <user_id>
146
+ AND user_type = <user_type>`
147
+ Use `%L` for user_id and user_type values in the WHERE clause.
148
+
149
+ 3. Call `con.query(queryText)` inside a try/catch. No values array —
150
+ the query is fully constructed by pg-format.
151
+
152
+ 4. On success — return `result.rowCount`.
153
+
154
+ 5. On error — log at error level with function name and error. Return
155
+ `null`.
156
+
157
+ ---
158
+
159
+ ### async addDeviceInformation(params)
160
+
161
+ **Purpose**: Inserts a new record into `tbl_user_deviceinfo` with the
162
+ provided field values. Returns the new record's id.
163
+
164
+ **Parameters**:
165
+ - `params` — plain object where keys are column names and values are
166
+ the values to insert. Must include at minimum: `user_id`, `user_type`,
167
+ `token`, `version`.
168
+
169
+ **Flow**:
170
+
171
+ 1. Extract keys and values from the params object.
172
+ Build `columns` string by joining keys with `, `.
173
+ Build `placeholders` string as `$1, $2, $3...` matching key count.
174
+
175
+ 2. Build the INSERT query using `format`:
176
+ `INSERT INTO tbl_user_deviceinfo (<columns>) VALUES (<placeholders>)
177
+ RETURNING id`
178
+ Use `format` only for the column names — use parameterized
179
+ placeholders for values.
180
+
181
+ 3. Call `con.query(queryText, valuesArray)` inside a try/catch.
182
+
183
+ 4. On success — return `result.rows[0].id`.
184
+
185
+ 5. On error — log at error level. Return `null`.
186
+
187
+ ---
188
+
189
+ ### async checkUpdateDeviceInfo(user_id, user_type, params)
190
+
191
+ **Purpose**: Updates or creates the device info record for a user with
192
+ device-specific fields. Used by login to persist device metadata
193
+ separately from the token update.
194
+
195
+ **Parameters**:
196
+ - `user_id` — numeric user identifier
197
+ - `user_type` — string role/type
198
+ - `params` — object containing device fields from the request body
199
+
200
+ **Flow**:
201
+
202
+ 1. Build `upd_device` object with these keys using nullish coalescing
203
+ for optional fields:
204
+ - `uuid` from `params.uuid` — default empty string
205
+ - `ip` from `params.ip` — default empty string
206
+ - `os_version` from `params.os_version` — default empty string
207
+ - `model_name` from `params.model_name` — default empty string
208
+ - `device_type` from `params.device_type` — no default, required
209
+ - `device_token` from `params.device_token` — default empty string
210
+
211
+ 2. Call `await this.checkDeviceInfo(user_id, user_type)`.
212
+
213
+ 3. If record exists — call `await this.updateDeviceInfo(user_id,
214
+ user_type, upd_device)`. Return the result.
215
+
216
+ 4. If record does not exist — add `user_id` and `user_type` to
217
+ `upd_device`, then call `await this.addDeviceInformation(upd_device)`.
218
+ Return the result.
219
+
220
+ ---
221
+
222
+ ### isEmptyObject(obj)
223
+
224
+ **Purpose**: Synchronous helper. Returns true if the given object has
225
+ no own enumerable keys, false otherwise.
226
+
227
+ **Parameters**:
228
+ - `obj` — any value, expected to be a plain object
229
+
230
+ **Flow**:
231
+ Return `!Object.keys(obj).length`.
232
+ This is the only synchronous function in the object. Not async.
233
+
234
+ ---
235
+
236
+ ### async randomOtpGenerator()
237
+
238
+ **Purpose**: Generates a random 4-digit integer for use as an OTP code.
239
+
240
+ **Flow**:
241
+ Return `Math.floor(1000 + Math.random() * 9000)`.
242
+ Range is always 1000–9999 inclusive. Never returns a 3-digit number.
243
+
244
+ ---
245
+
246
+ ### async createRecords(tablename, records)
247
+
248
+ **Purpose**: Generic INSERT helper. Inserts a single record into any
249
+ table and returns the new record's id. Used by model files for simple
250
+ single-row inserts where no special logic is needed.
251
+
252
+ **Parameters**:
253
+ - `tablename` — the target table name string
254
+ - `records` — plain object where keys are column names and values are
255
+ the values to insert
256
+
257
+ **Flow**:
258
+
259
+ 1. Extract keys and values from `records`.
260
+ 2. Build columns and parameterized placeholders the same way as
261
+ `addDeviceInformation`.
262
+ 3. Build query: `INSERT INTO <tablename> (<columns>) VALUES
263
+ (<placeholders>) RETURNING id`
264
+ Use `format` for column names only. Use parameterized values.
265
+ 4. Call `con.query(queryText, valuesArray)` in try/catch.
266
+ 5. On success — return `result.rows[0].id`.
267
+ 6. On error — log at error level. Return `null`.
268
+
269
+ ---
270
+
271
+ ### async updateRecords(tablename, updparams, where)
272
+
273
+ **Purpose**: Generic UPDATE helper. Updates records in any table
274
+ matching the given WHERE clause string. Returns the number of rows
275
+ affected.
276
+
277
+ **Parameters**:
278
+ - `tablename` — the target table name string
279
+ - `updparams` — plain object of column names and new values
280
+ - `where` — raw SQL WHERE clause string without the WHERE keyword.
281
+ Example: `"user_id = 42 AND is_deleted = FALSE"`
282
+ Caller is responsible for constructing a safe WHERE clause.
283
+
284
+ **Flow**:
285
+
286
+ 1. Build SET clause using `pg-format` the same way as `updateDeviceInfo`.
287
+ 2. Build query: `UPDATE <tablename> SET <setClause> WHERE <where>`
288
+ 3. Call `con.query(queryText)` in try/catch.
289
+ 4. On success — log at info level with rowCount. Return `result.rowCount`.
290
+ 5. On error — log at error level. Return `null`.
291
+
292
+ ---
293
+
294
+ ### async insertNotification(notification)
295
+
296
+ **Purpose**: Stub function. Placeholder for persisting notification
297
+ records to the database. Returns `true` always. Developer implements
298
+ the actual insert logic when the notifications table is created.
299
+
300
+ **Parameters**:
301
+ - `notification` — the notification object. Received but not used in
302
+ the stub.
303
+
304
+ **Flow**:
305
+ Log at info level: insertNotification stub called.
306
+ Return `true`.
307
+
308
+ ---
309
+
310
+ ## Export
311
+ ```
312
+ module.exports = common
313
+ ```
314
+
315
+ The plain object is exported directly. Imported everywhere as:
316
+ `const common = require('../config/common')`
317
+ or
318
+ `const common = require('./common')` depending on caller depth.
319
+
320
+ ---
321
+
322
+ ## What This File Does NOT Do
323
+
324
+ - Does not import encryption utilities — no crypto operations here
325
+ - Does not import response.js — no req/res objects in any function
326
+ - Does not define business logic — only shared infrastructure helpers
327
+ - Does not validate input parameters — callers are responsible for
328
+ passing correct values
329
+ - Does not throw from any function — all errors are caught, logged,
330
+ and return null
@@ -0,0 +1,123 @@
1
+ ---
2
+ type: task
3
+ name: generate-constants
4
+ agent: nodejs-agent
5
+ ---
6
+
7
+ # File: config/constants.js
8
+
9
+ ## Purpose
10
+ Defines all application-wide constant values in a single frozen object
11
+ exported as GLOBALS. Every file in the service that needs a shared
12
+ constant imports this module. No magic strings or hardcoded values exist
13
+ anywhere else in the codebase — they all live here. The object is frozen
14
+ using Object.freeze so no file can accidentally mutate it at runtime.
15
+
16
+ ---
17
+
18
+ ## Dependencies
19
+
20
+ No imports from within the service. Only reads from process.env which
21
+ is already loaded by dotenv in app.js before this module is required.
22
+
23
+ Two intermediate variables are derived from process.env before the
24
+ GLOBALS object is built, because multiple keys inside GLOBALS depend
25
+ on them:
26
+
27
+ ### BASE_URL
28
+ Read from `process.env.BASE_URL`. Default to empty string if absent.
29
+ Used to construct all image URL constants that are served from the
30
+ base domain.
31
+
32
+ ### S3_BUCKET_ROOT
33
+ Read from `process.env.S3_BUCKET_ROOT`. Default to empty string if
34
+ absent. Used to construct all S3-hosted asset path constants.
35
+
36
+ ---
37
+
38
+ ## GLOBALS Object
39
+
40
+ Built as a single Object.freeze call. Keys and their values:
41
+
42
+ ### Identity
43
+ - `APP_NAME` — the human-readable name of the service. Read from
44
+ `process.env.PROJECT_NAME`. This is the same value as the service
45
+ name set during `@initialize-project`. Used in email templates,
46
+ log messages, and push notification titles.
47
+
48
+ ### Security
49
+ - `API_KEY` — the expected API key value for incoming request validation.
50
+ Read from `process.env.API_KEY`.
51
+ - `KEY` — the AES encryption key. Read from `process.env.KEY`.
52
+ - `IV` — the AES initialization vector. Read from `process.env.IV`.
53
+
54
+ ### URLs
55
+ - `BASE_URL` — the intermediate variable value. The base domain URL
56
+ for this service. Used to construct asset URLs.
57
+ - `S3_BUCKET_ROOT` — the intermediate variable value. Root path for
58
+ all S3-hosted assets.
59
+
60
+ ### Image Paths
61
+ - `LOGO` — constructed as `BASE_URL + 'images/logo_white.png'`. Used
62
+ in email templates as the header logo.
63
+ - `MESSAGE_LOGO` — constructed as `BASE_URL + 'images/email.png'`.
64
+ Used as a secondary email image asset.
65
+ - `ARROW_IMAGE` — constructed as `BASE_URL + 'images/arrow-right.gif'`.
66
+ Used in email templates for directional visual elements.
67
+ - `USER_IMAGE` — constructed as `S3_BUCKET_ROOT + 'users/'`. The S3
68
+ prefix path for all user-uploaded profile images.
69
+
70
+ ### Pagination
71
+ - `PER_PAGE` — default page size for all paginated queries. Value is
72
+ the string `'100'`. Stored as a string, not a number, for direct
73
+ use in query parameter comparisons.
74
+
75
+ ### Firebase
76
+ - `FIREBASE_ADMIN_JSONFILE` — the relative path to the Firebase service
77
+ account credentials file. Value is `'./pem/service-file.json'`.
78
+ This file is gitignored and must be manually placed by the developer.
79
+
80
+ ### App Store Links
81
+ - `ANDROID_APP` — URL to the Android app store listing. Default value
82
+ is `'#'` as a placeholder. Developer replaces when the app is
83
+ published.
84
+ - `IOS_APP` — URL to the iOS app store listing. Default value is `'#'`.
85
+
86
+ ### Legal and Email Links
87
+ - `UNSUBSCRIBE` — base URL for the email unsubscribe endpoint. Default
88
+ `'#'`. The full unsubscribe URL is constructed in templates by
89
+ appending the encoded user ID.
90
+ - `TERMS_OF_USE` — URL to the terms of use page. Default `'#'`.
91
+ - `PRIVACY_POLICY` — URL to the privacy policy page. Default `'#'`.
92
+
93
+ ---
94
+
95
+ ## .env additions required by this file
96
+
97
+ Add to the `.env` template in nodejs-agent:
98
+ ```
99
+ BASE_URL=https://yourservice.com/
100
+ S3_BUCKET_ROOT=https://yourbucket.s3.amazonaws.com/
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Export
106
+ ```
107
+ module.exports = Globals;
108
+ ```
109
+
110
+ The frozen object is exported directly. Imported everywhere as:
111
+ `const GLOBALS = require('../config/constants')`
112
+
113
+ ---
114
+
115
+ ## What This File Does NOT Do
116
+
117
+ - Does not import from any other file in the service
118
+ - Does not contain any functions or logic — values only
119
+ - Does not define HTTP status codes — those belong in a separate
120
+ constants section if needed, not in this file
121
+ - Does not define route paths — those are defined inline in route.js
122
+ files
123
+ - Does not mutate after load — Object.freeze enforces this