Skip to content

03 — designer-api architecture

A guided tour of the folder structure, the entry point, and how a request flows.


Folder map

designer-api/
├── conf.js                        ← 27 lines — env var passthrough (smaller than someli-api's)
├── server.js                      ← 70 lines — Express boot, MySQL connect, mount routes
├── package.json                   ← 30 runtime deps, 0 dev deps
├── Dockerfile                     ← multi-stage; runs nginx + node behind it
├── .dockerignore
├── nginx.conf
├── push.sh                        ← manual SSH-deploy helper
├── favicon.ico
├── routes/
│   └── routes.js                  ← 13608 lines, 269 endpoints, one Api class
├── actions/
│   └── actions.js                 ← generic CRUD (older fork of someli-api's)
├── helper/
│   └── index.js                   ← 88 lines — small helper set; NO helper.js / aiLogics.js / constants.js / etc.
├── modules/
│   └── dbDriver/lib/mysql.js      ← callback MySQL pool wrapper
├── conf/
│   └── credentials.json           ← likely Google service-account JSON
├── content_generation_bot.js      ← standalone AI worker (OpenAI)
├── post_image_generation1_bot.js
├── FAQsbot.js
├── quizbot.js
├── quotesbot.js
├── trendsbot.js
├── teamsnotification.js           ← Slack notifier on missing content
├── generateTextContent.js
├── pdfToExtractData.js
├── polotno_image_uploader.js
├── post_approve_job.js
├── s3_Image_upload.js
├── test.js                        ← ad-hoc test script
├── deploy_image.py                ← Python helper (not part of runtime)
└── job_*.js                       ← ~57 background workers
                                     ├── 30+ industry-clones (job_auto_garge_*, job_disability_insurance, …)
                                     └── several misc workers

Entry point — server.js

The boot sequence (70 lines):

const conf = require("./conf");
const express = require("express");
const bodyparser = require("body-parser");
const cors = require("cors");
const port = process.env.port || conf.port || 5002;
const app = express();
const fileUpload = require('express-fileupload');

app.use(cors());                                          // 1. CORS middleware
app.use((req, res, next) => {                             // 2. Manual CORS header (redundant)
    res.header('Access-Control-Allow-Origin', '*');
    res.header('"Access-Control-Allow-Credentials" : true');  // BUG — this string is the header NAME
    next();
});
app.use(fileUpload());                                    // 3. File upload
app.use(bodyparser.json({ limit: '50MB' }));              // 4. JSON parsing
app.use(bodyparser.urlencoded({ extended: true }));       // 5. URL-encoded forms
app.use('/favicon.ico', express.static('./favicon.ico')); // 6. Static
app.use('/uploads', express.static('uploads'));

var db = require('./modules/dbDriver/lib/mysql');         // 7. MySQL pool
db.connect(conf, (res) => {
    if (!res && !res.status) console.log("\n Something Went Wrong!");
});

var server = app.listen(port, ...);                       // 8. Listen

const io = require('socket.io')(server);                  // 9. Socket.IO
io.on('connection', function (socket) { ... });

var App = { db, server: app, socket: socketConnection };  // 10. Shared App
var { api } = require("./routes/routes");                 // 11. Mount routes
var Api = new api(App);
Api.init();

Differences from someli-api/server.js (very important)

  • No routes/auth.js mount. Auth endpoints are inside routes/routes.js.
  • No webhook body-parser exemption. No Paddle / Stripe handlers.
  • No module.exports.appData = App. dashboard/-style runtime cross-requires don't work here. Each file connects to MySQL directly.
  • Double CORS headers. The cors() middleware AND a manual res.header(...). The manual line's second header is malformed (the entire "Access-Control-Allow-Credentials" : true string is the header name with no value).
  • 50 MB body limit (vs someli-api's 150 MB).
  • No /health, no /db-health endpoints.

Route file — routes/routes.js

A single 13608-line file containing:

  • ~25 lines of require()s (AWS, Polotno, OpenAI, Unsplash, mysql2, …)
  • Module export at line 84: module.exports = { api: apiRoutes, con, s3 } — exposes con (sync-mysql) and s3 for other files
  • apiRoutes.prototype.init = function() { ... } at line 91 — registers all 269 endpoints inline
  • All handlers inline; no per-file extraction
// Typical endpoint (illustrative)
App.server.post('/myEndpoint', async (req, res) => {
  // ... auth check (hand-rolled, not Passport / not Bearer JWT) ...
  const rows = con.query("SELECT ... FROM ... WHERE ?", [req.body.id]);
  res.json({ status: 200, data: rows });
});

Junior gotcha: everything is in this one file. When you grep, expect noisy results. Use line numbers from the audit's ../../audit/designer-api/API-inventory.md to navigate.


Request lifecycle

HTTP request
nginx (production) or Express directly (dev)
[middleware: CORS → fileupload → bodyparser → static]
routes/routes.js handler  (matched by path)
hand-rolled auth check (looks at request header, validates)
SQL query (sync-mysql `con.query(...)` is the common pattern)
JSON response

No Passport, no JWT, no AES decryption. Auth is much simpler than someli-api.


Background workers

Each job_*.js is independent:

// Typical job file
require('dotenv').config();
const cron = require('node-cron');
const mysql = require('sync-mysql');
const con = new mysql({ host, user, password, database });

cron.schedule("0 */2 * * *", async () => {
  // every 2 hours, do something
  const rows = con.query("SELECT ...");
  for (const row of rows) {
    // OpenAI call, S3 upload, etc.
  }
});

Industry-clone pattern

30+ of these files are near-duplicates. Example shape (illustrative):

// job_auto_garge_post_generation.js
const INDUSTRY_ID = 42;          // hardcoded
// ... otherwise identical to job_disability_insurance_post_generation.js (INDUSTRY_ID = 17)

This is a known refactor target. See ../../audit/designer-api/jobs-inventory.md.

Bots vs jobs

  • Jobs (job_*.js) follow the node-cron pattern: schedule + handler.
  • Bots (FAQsbot.js, quizbot.js, etc.) are standalone runnable processes that interact with OpenAI in larger batches. Some are scheduled, some run on demand.

Connection count

57 jobs × 1 MySQL connection each + 6 bots × ~1 each + the main server pool = ~70 idle MySQL connections. Within limits but not free.


Helpers

helper/index.js is 88 lines — much smaller than someli-api/helper/. Contains:

  • Auth-token helpers
  • A few utility functions

There is no helper/helper.js, no helper/aiLogics.js, no helper/constants.js etc. — the patterns from someli-api were not copied across.

Consequence: when you need a utility, don't assume it exists here. Either it's in helper/index.js, inlined in routes/routes.js, or simply absent.


Notifications — teamsnotification.js

A standalone Slack / Teams notifier scheduled to ping a channel when content is missing.

// teamsnotification.js (illustrative — actual values are different but hardcoded)
const token = 'xoxb-...';        // HARDCODED Slack bot token (known finding)
const channel = 'C...';

Don't add new hardcoded credentials. Use env vars. The existing hardcoded ones are flagged for cleanup.


Code-overlap with someli-api

File Status
routes/routes.js Same skeleton, completely different content
actions/actions.js Old fork of someli-api/actions/actions.js; drifted
helper/index.js Drifted; different content from someli-api/helper/index.js
modules/dbDriver/lib/mysql.js Drifted from someli-api's version

Zero filename overlap between someli-api/job_*.js and designer-api/job_*.js. The job inventories are completely disjoint. See ../../audit/CODE-OVERLAP-MATRIX.md.


Where to put a new file

Adding Put it in
A new REST endpoint routes/routes.js (the only routes file) inside apiRoutes.prototype.init()
A new background job New file job_<name>.js at repo root + tell ops how to add it to PM2
A new bot New file <name>bot.js at repo root
A new helper function helper/index.js (or a new file helper/<topic>.js and require it)
A new notification target Extend teamsnotification.js or create a new module — use env vars, never hardcode

Next

04-getting-started.md.