Architecture Overview¶
Technology Stack¶
- Runtime: Node.js (v20.18.1) with Express 4.x
- Module System: CommonJS (
require/module.exports) - Database: MySQL/MariaDB
- Process Manager: PM2 (production), Nodemon (development)
- Container: Docker (Node 20.18.1-slim + nginx), port 80/8080
Entry Point & Startup Sequence¶
File: server.js
- Load environment variables via
dotenvfromconf.js - Create Express app with middleware stack (verified order):
express-session(with hardcoded secret — see security.md)express-fileupload(multipart uploads)express.json({ limit: '150mb', parameterLimit: 50000 })— applied except on webhook routes- Static routes (
/favicon.ico,/uploads) - Healthcheck endpoints (
/health,/db-health) passport.initialize()+passport.session()cors()(default options — accepts all origins; see security.md)
⚠ Earlier versions of this doc claimed
helmetis part of the middleware stack and thatbody-parseris set to a 50 MB limit. Both are wrong:helmetis not loaded at all, and the body limit is 150 MB (set viaexpress.json, notbody-parser). The mismatch with nginx's 100 MB limit (nginx.conf) is itself a finding — see deployment.md §6.6. 3. Connect to MySQL usingmodules/dbDriver/lib/mysql.js(async pool,mysqlpackage) 4. Initialize Socket.IO on the HTTP server 5. Build the sharedAppobject:{ db, server: app, socket: io }6. ExportappData = { db, server: app, socket: io }for use across modules 7. Mount route modules in order: -/auth/->routes/auth.js(authenticated user routes) -/social/->routes/social.js(OAuth flows) -/paddle/->routes/paddle.js(Paddle billing) -/partnerAuth/->routes/partnerAuth.js(partner API) -/auth->dashboard/routes/index.js(analytics/insights, shares prefix with auth) -/(root) ->routes/routes.js(main API, class-based pattern) 8. Start HTTP server onconf.PORT
Route Architecture¶
Route Files & Mount Points¶
| File | Mount Point | Pattern | Auth | Route Count |
|---|---|---|---|---|
routes/routes.js |
/ |
Class-based (apiRoutes.prototype.init()) |
1 route uses methods.ensureToken; rest have no route-level auth |
394 |
routes/auth.js |
/auth/ |
Express Router | router.use(auth) on all routes |
291 |
routes/social.js |
/social/ |
Express Router | None (Passport session-based OAuth) | 15 |
routes/paddle.js |
/paddle/ |
Express Router | router.use(auth) on all routes |
2 |
routes/partnerAuth.js |
/partnerAuth/ |
Express Router | Custom JWT per-route | 4 |
dashboard/routes/index.js |
/auth |
Express Router | router.use(auth) only in dev mode |
22 |
The routes/routes.js Pattern¶
This file exports { api: apiRoutes, con } where:
- apiRoutes is a constructor that receives the App object
- Routes are defined inside apiRoutes.prototype.init(), binding directly to App.server (the Express app)
- con is a sync-mysql connection used throughout for synchronous queries
- Routes are bound at the root path, so /uploadImage is accessible at http://host/uploadImage
The routes/auth.js Pattern¶
- Standard Express Router exported as
module.exports = router router.use(auth)applied globally at line 78, protecting all routes- Auth middleware sets
userId,accountId,role,isPersonnelon theresobject (notreq) - Handlers destructure
{ userId, accountId }fromres
Webhook Routes¶
Four routes in routes/routes.js use express.raw() body parser for signature verification:
- POST /stripe_webhooks
- POST /paddle_sandbox_webhooks
- POST /paddle_production_webhooks
- POST /paddle_Admin_webhooks
Authentication & Authorization¶
Token Flow¶
- Login (
routes/routes.js): User credentials verified againsttMembertable - Token Generation (
helper/tokenGenerator.js): JWT payload ({ userId, role, accountId, isPersonnel }) is signed withjsonwebtoken, then AES-encrypted withcrypto-js - Token Verification (
middlewares/auth.js): Bearer token is AES-decrypted, then JWT-verified. Extracted fields are set onres(notreq) - Account Check: Many handlers call
checkuseraccount(userId, accountId)to validate the user belongs to the specified account
OAuth (Passport.js)¶
- File:
middlewares/passport.js - Strategies: Google, GitHub, Facebook, LinkedIn, TikTok, Twitter
- Flow:
routes/social.jsinitiates OAuth -> callback encrypts user data withtokenGenerator-> redirects to client with token in query string
Alternative Auth¶
methods.jsexportsensureTokenmiddleware (Bearer token extraction) used byGET /inroutes/routes.jsroutes/partnerAuth.jsuses its own JWT signing/verification for partner API authentication
Database Access Layer¶
Three MySQL access patterns coexist:
| Package | Type | Where Used | Connection |
|---|---|---|---|
mysql |
Async (callbacks) | modules/dbDriver/lib/mysql.js -> actions/actions.js |
Connection pool via App.db |
sync-mysql |
Synchronous/blocking | routes/routes.js (con), routes/auth.js (con), most job_*.js files |
Direct connection, created at module load |
mysql2/promise |
Async (promises) | Some newer job files, dashboard services | mysql.createPool() with await |
Generic CRUD Layer¶
File: actions/actions.js
Provides table-agnostic operations via the dbDriver:
- getAll(tableName, conditions, fields, limit, offset)
- insertData(tableName, fields)
- updateData(tableName, fields, conditions)
- deleteData(tableName, conditions)
- customQuery(rawSQL)
Used by route handlers: actions.getAll(...), actions.insertData(...), etc.
Direct SQL¶
Most route handlers and all background jobs execute SQL directly via con.query() (sync-mysql) or pool queries, bypassing the CRUD layer.
Background Jobs¶
Configuration: ecosystem.config.js defines ~37 PM2-managed processes.
Jobs are standalone Node.js scripts in the project root (e.g., job_facebook_publish.js, job_content_generation.js). Each job:
- Creates its own database connection (sync-mysql or mysql2)
- Registers a
node-cronschedule (intervals range from every second to daily) - Polls a database table (usually
tJobsortContentPlanner) for pending work - Processes items and updates status
Job Categories¶
- Social Publishing: Post images/videos/carousels to Facebook, Instagram, LinkedIn, TikTok
- Content Generation: AI-powered content creation using OpenAI, Gemini, Bedrock (Llama/Claude/Nova)
- Media Generation: Render Polotno templates into images, generate thumbnails
- Token Management: Refresh OAuth tokens for Facebook, LinkedIn, TikTok, Vertex AI
- Analytics: Fetch post/account insights from social platforms
- Billing: Process Paddle/Stripe webhooks, invoice handling
- Notifications: Send emails (SendGrid) and Slack alerts
Configuration¶
File: conf.js
Loads all environment variables via dotenv and exports them. Key config groups:
- Database: host, user, password, database, dbPort
- AWS: AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, S3_BUCKET, S3_Region
- AI: OPENAI_API_KEY, GOOGLE_API_KEY, AWS_BEDROCK_*
- Payments: PADDLE_API_KEY, PADDLE_WEBHOOK_KEY
- Social: FACEBOOK_APP_ID, LINKEDIN_CLIENT_ID, TWITTER_CLIENT_ID, TIKTOK_CLIENT_ID
- Email: SENDGRID_API_KEY
Helper Module Structure¶
Entry point: helper/index.js (re-exports from submodules)
| Module | Purpose |
|---|---|
helper/helper.js |
Core utilities: response formatting, S3 operations, image processing, Paddle config |
helper/aiLogics.js |
AI provider wrappers: openAiLogic, geminiLogic, LlamaFunction, claudeFunction, novaFunction |
helper/tokenGenerator.js |
JWT + AES token encryption/decryption |
helper/constants.js |
Provider IDs, plan constants, template mail IDs |
helper/basic.js |
Generic utilities: shuffleArray, downloadVideoToBuffer, extractAndParseJSON |
helper/stockImage.js |
Pexels/Pixabay stock image fetching |
helper/webScraping.js |
Website content extraction via axios + Cheerio |
helper/webscrapeHomePage.js |
Website scraping via Puppeteer (JavaScript-rendered pages) |
helper/ragProcess.js |
RAG pipeline: document chunking, TF-IDF, embeddings |
helper/getRagData.js |
RAG query answering via Bedrock |
helper/social.js |
Social media helpers: addSPosts, tiktokRefresh |
helper/files_upload.js |
S3 and GCS file upload functions |
helper/functionsForAi/gemini.js |
Gemini API wrappers with Google Search grounding |
helper/functionsForAi/cloudRag.js |
Vertex AI RAG corpus queries |
helper/postValidation.js |
Post image color/content validation |
Standard Response Format¶
All API responses follow this structure:
{
"status": true, // boolean success/failure
"errorMsg": "", // error message if status is false
"response": {} // payload data
}
Constructed via getSuccessResponse(data) and getErrorResponse(message) from helper/helper.js.
Real-time Communication¶
Socket.IO is initialized in server.js and passed through the App object. Used for broadcasting updates to connected clients (e.g., content generation status, scheduling updates).
File Storage¶
- Primary: AWS S3 (two buckets/regions configured via
S3_BUCKET,S3_Region,S3_BUCKET2,S3_Region2) - Secondary: Google Cloud Storage (for RAG/knowledge base files, configured via
CLOUD_BUCKET_NAME) - Upload functions:
s3UploadFunctionandgcsUploadFunctioninhelper/files_upload.js
Deployment¶
- Dockerfile: Node 20.18.1-slim base, runs nginx reverse proxy + Node app
- Jenkinsfile: CI/CD pipeline deploying
dev_apibranch via SSH + PM2 - Branches:
dev_api(development),uat_api(staging),main(production)