02 — someli-api stack¶
What someli-api actually is, library by library.
Runtime and language¶
| Runtime | Node.js 20.18.x (production image: node:20.18.3-slim) |
| Module system | CommonJS — require() / module.exports. There is no TypeScript in this repo. |
| Process manager (production) | PM2 via ecosystem.config.js |
| Process manager (development) | nodemon (auto-restarts on file change; invoked by yarn start) |
| Container | Docker — Dockerfile exists; production runs Docker + nginx fronting node |
Note: no TypeScript means no compile-time type safety. The codebase relies on convention and grep. A common bug is passing fields with the wrong name (
accountIDvsaccountId) — these only surface at runtime.
HTTP framework¶
| Framework | Express 4.x |
| Body parsing | express.json({ limit: '150mb', parameterLimit: 50000 }) — large because the platform uploads media |
| File uploads | express-fileupload (multipart) |
| Sessions | express-session (used by Passport OAuth flows; secret is hardcoded — known finding) |
| CORS | cors() with default options (wildcard origin — known finding) |
| WebSockets | Socket.IO (legacy v2 / 4 — verify in package.json) — used for realtime UI updates |
The middleware ordering in server.js is important: webhook routes (/stripe_webhooks, /paddle_*) are exempted from JSON parsing so they can verify signatures against the raw body. Do not change the order unless you understand why.
Auth¶
| Internal auth | Custom: JWT (jsonwebtoken) signed, then AES-encrypted with crypto-js. Lives in helper/tokenGenerator.js. |
| Token transport | HTTP header named by TOKEN_HEADER_KEY env var (typically Token). Decrypted+verified in middlewares/auth.js. |
| Identity propagation | Middleware sets res.userId, res.accountId, res.role, res.isPersonnel (yes, on res, not req — read ../../audit/someli-api/authentication.md before being surprised). |
| OAuth | Passport.js in middlewares/passport.js. Strategies: Google, GitHub, Facebook, LinkedIn, TikTok, Twitter v2. |
| Sessions | Used only for the Passport OAuth handshake; not for the regular API. |
| Token revocation | In-memory map in helper/revokeToken.js. Clears on process restart → tokens are effectively long-lived. |
| Partner API auth | A different JWT in routes/partnerAuth.js (custom signing/verification). |
Junior gotcha: when reading a route handler, you'll often see code like
const { userId, accountId } = res;. That destructures fromres, which is unusual. Don't "fix" it — auth middleware writes there, and rewriting it touches the whole codebase.
Database¶
| Engine | MySQL (one shared instance across all four backends in production) |
| Schema | Reference snapshot at ../../audit/someli-api/someli-schema.sql |
| Migrations | None. Schema changes are applied to dev / UAT / prod manually. |
Three coexisting MySQL access patterns¶
| Library | Style | Where it's used |
|---|---|---|
mysql |
Async (callbacks) | modules/dbDriver/lib/mysql.js → actions/actions.js (pool via App.db) |
sync-mysql |
Synchronous / blocking | routes/routes.js (con), routes/auth.js (con), most job_*.js files |
mysql2/promise |
Async (promises) | newer job files, dashboard services |
Junior gotcha:
sync-mysqlblocks the entire event loop on every query. It's used pervasively. Don't be surprised when you seeconst rows = con.query(...)with noawait. Do not introduce newsync-mysqlusage; prefermysql2/promisefor any new code.
CRUD layer¶
actions/actions.js exposes a generic, table-agnostic CRUD:
actions.getAll(tableName, conditions, fields, limit, offset)
actions.insertData(tableName, fields)
actions.updateData(tableName, fields, conditions)
actions.deleteData(tableName, conditions)
actions.customQuery(rawSQL)
Most route handlers and all background jobs bypass this layer and run direct SQL via con.query(...). The CRUD helper is convenient but not universally used.
AI providers¶
| Provider | Purpose | Where |
|---|---|---|
| AWS Bedrock | Claude / Llama / Nova for text generation | helper/aiLogics.js |
| Google Vertex / Gemini | RAG retrieval and Gemini text | helper/ragProcess.js, agents |
| OpenAI | image and text generation | scattered in routes and jobs |
Polotno SDK (polotno-node) |
server-side image rendering | image generation jobs |
The RAG pipeline uses Google Cloud RAG (Vertex) with a corpus per account — see ../../audit/someli-api/rag-pipeline.md.
The agents (agents/conversationAgent.js, researchAgent.js, profileAgent.js, inputParserAgent.js) wrap multi-step LLM workflows — see ../../audit/someli-api/agents-and-ai.md.
Background workers¶
| Pattern | Standalone job_*.js files at the repo root |
| Scheduling | PM2 + node-cron inside the script |
| Process manifest | ecosystem.config.js |
| Number of jobs | ~108 |
| Brokers / queues | None — jobs poll the DB directly |
Each job file is self-contained: requires dotenv (via conf.js), opens its own MySQL connection (typically sync-mysql), registers a cron.schedule(...), and runs.
Consequence: 108 jobs × 1 MySQL connection each = 108 idle DB connections plus the main server pool. Stays manageable in prod, but be aware.
See ../../audit/someli-api/jobs-inventory.md for the full list.
External integrations¶
| Service | Used for | Library |
|---|---|---|
| AWS S3 | Media + template storage (two buckets, two regions) | aws-sdk |
| SendGrid | Transactional email | @sendgrid/mail |
| Paddle | Billing (sandbox + prod) | Paddle REST API |
| Stripe | Legacy billing | stripe |
| Facebook / Instagram | Publishing + OAuth | passport-facebook, FB Graph API |
| Publishing + OAuth | passport-linkedin-oauth2 |
|
| TikTok | Publishing + OAuth | TikTok API direct |
| X / Twitter v2 | Publishing + OAuth | Twitter v2 API direct |
| Slack | Internal notifications | Slack Web API |
| Google Cloud RAG | Knowledge-base retrieval | Google Cloud SDK |
| Polotno (cloud + node) | Image rendering | polotno-node, Polotno Cloud REST |
| Impact (affiliate) | Affiliate tracking | direct REST |
The full integration inventory: ../../audit/someli-api/Integration-inventory.md.
Logging and observability¶
| Logger | console.log (no structured logger) |
| Format | Plain string |
| Sinks | stdout → PM2 log files in production |
| Error tracking | None (no Sentry / Rollbar / etc.) |
| Healthchecks | GET /health, GET /db-health |
| Tracing | None |
Junior gotcha: there is no central logger. Logs are ad-hoc
console.logs scattered throughout the codebase. This means: (1) PII can be logged inadvertently (a known finding); (2) you cannot adjust log levels without code edits. Don't addconsole.logof user data when debugging.
See ../../audit/someli-api/logging-observability.md.
Build, test, deploy¶
| Build | None — Node runs source directly |
| Lint | No ESLint configured |
| Tests | No test suite. package.json's test script just exit 1. |
| CI | Jenkins (operational) + GitHub Actions (.github/workflows/dev-api-deploy.yml) |
| Deploy | Jenkins SSH-deploys to AWS Lightsail; pulls the branch, builds, restarts PM2 |
| Branches | dev_api (dev), uat_api (uat), main (prod) |
Consequence for you: if you change a function in
helper/helper.js, nothing automated will catch a regression. You must manually exercise the code paths that use it. There is no safety net.
Next¶
→ 03-architecture.md — folder map, entry point, request lifecycle.