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,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
|