Architecture Overview¶
Purpose¶
A standalone Express + MySQL service that exposes the HTTP API for the Admin Console (admin_console_R). It is not a public-facing API; it is for Someli's internal staff (admin, customer success, account managers, developers, designers — see role taxonomy below).
The repo is a fork of the someli-api skeleton, trimmed and adapted for admin workloads. The shared lineage shows up file-by-file (see code-overlap.md).
Folder layout¶
Someli-admin-api/
├── conf.js # 49 lines — env var passthrough
├── server.js # 70 lines — Express boot, Socket.IO, MySQL connect, mount routes
├── methods.js # 16 lines — JWT Bearer-token middleware (`ensureToken`)
├── package.json # 78 runtime deps + 1 devDep
├── Nova-Pro_KnowledgeBased_Content.py # batch processor for AWS Bedrock Nova
├── README.md # 1 line (placeholder)
├── routes/
│ ├── routes.js # 1400 lines — main route surface; 31 endpoints
│ └── auth.js # 957 lines — 9 endpoints; mounted at /auth/
├── actions/
│ └── actions.js # generic CRUD service layer
├── middlewares/
│ ├── auth.js # token-header decryption middleware
│ └── validation.js # express-validator chains
├── helper/ # 10 files (see code-overlap.md for which are shared)
│ ├── aiLogics.js
│ ├── basic.js
│ ├── constants.js
│ ├── helper.js
│ ├── index.js
│ ├── ragProcess.js # byte-identical to someli-api's
│ ├── revokeToken.js # byte-identical to someli-api's
│ ├── stockImage.js
│ ├── tokenGenerator.js # byte-identical to someli-api's
│ └── webScraping.js # byte-identical to someli-api's
├── modules/
│ └── dbDriver/lib/mysql.js # callback-style MySQL pool wrapper
└── conf/
└── credentials.json # likely Google service account or similar
There are no background jobs in this repo — no job_*.js files at the root. All background work lives in someli-api (where its scheduling and PM2 setup is).
Entry point — server.js¶
const conf = require("./conf");
const express = require("express");
const bodyparser = require("body-parser");
const cors = require("cors");
const expressSession = require('express-session');
const port = process.env.port || conf.port || 5002;
const app = express();
const fileUpload = require('express-fileupload');
app.use(expressSession({
secret: "3eB(2:\srlI+qa5", // hardcoded — see security.md
resave: false,
saveUninitialized: true
}))
app.use(fileUpload());
app.use((req, res, next) => {
if (req.originalUrl === '/stripe_webhooks' || req.originalUrl === '/paddle_sandbox_webhooks' || req.originalUrl === '/paddle_production_webhooks') {
next(); // these need raw body for signature verification
} else {
express.json({ limit: '150mb', extended: true, parameterLimit: 50000 })(req, res, next);
}
});
app.use('/favicon.ico', express.static('./favicon.ico'));
app.use('/uploads', express.static('uploads'));
var db = require('./modules/dbDriver/lib/mysql');
db.connect(conf, (res) => {
if (!res && !res.status) console.log("\n Something Went Wrong!");
});
var server = app.listen(port, …);
const io = require('socket.io')(server);
io.on('connection', function (socket) { … });
var App = { db, server: app, socket: socketConnection };
module.exports.appData = App;
app.use(cors());
const authRoute = require("./routes/auth");
app.use("/auth/", authRoute);
var { api } = require("./routes/routes");
var Api = new api(App);
Api.init();
The boot sequence mirrors someli-api/server.js almost exactly: bodyparser-ordering for webhook signature verification, the App object exported as appData, the routes/auth.js mounted at /auth/, the main routes/routes.js registered via new api(App).init().
Key drift from someli-api: this repo has no Passport setup (the imports are commented out), no routes/social.js mount, no routes/paddle.js mount, no dashboard/ mount.
Two route surfaces¶
| File | Mount | Auth | Endpoint count | Notes |
|---|---|---|---|---|
routes/auth.js |
/auth/* (via app.use("/auth/", authRoute)) |
router.use(auth) at top — every endpoint requires a decrypted token |
9 | High-permission admin operations (user/account delete, search across all users) |
routes/routes.js |
(root via api.init()) |
Mixed — some endpoints have methods.ensureToken (Bearer JWT) per-handler, some are unauthenticated (e.g., /authenticate, /webauthenticate) |
31 | Login + most CRUD on personnel, roles, accounts, affiliate marketing |
The two-layer auth (token-header middleware in one file, Bearer JWT in the other) is unusual. See authentication.md.
Database access¶
Three patterns coexist (same as someli-api):
- Callback-style MySQL pool via
modules/dbDriver/lib/mysql.js(used throughApp.db) mysql2.createConnectionfor direct queriesmysql2/promisepool for async/await queries
Plus sync-mysql is pulled in via helpers (helper/helper.js, etc.). All four DB pathways point at the same MySQL instance as someli-api / designer-api (configuration is via the same env vars — see configuration.md).
Socket.IO¶
Boot includes a Socket.IO server, but no handlers in this codebase consume socket events beyond the standard connect / disconnect / update echo. This is vestigial code from the someli-api fork.
Role taxonomy¶
Roles are integers stored in tMember.role_type. The admin FE pins the IDs in env vars (admin_console_R/src/config/env.ts):
SUPER_ADMINADMINACCOUNT_MANAGERDEVELOPERDESIGNER- (plus customer-facing roles managed elsewhere, e.g.,
role_type = 11for "test users")
This API checks role on a per-endpoint basis inside handlers (e.g., routes/auth.js:127 — getAllOwnerList filters by role == 'test' to scope results). There is no role-based middleware; access is by SQL filter only.