Tech Stack & Architecture

Technical overview for developers and recruiters.

LayerChoice
RuntimeNode.js 22+
FrameworkExpress 5, TypeScript
DatabaseMySQL 8 (mysql2 — no ORM, hand-written parameterized SQL)
ViewsEJS + express-ejs-layouts
ValidationZod
StylingSass → Bootstrap 5 grid + hand-written components

Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                         Client (Browser)                         │
│                    HTML + Bootstrap grid + vanilla JS             │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Node.js / Express (TypeScript)                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Middleware chain                                          │  │
│  │  fake headers → trust proxy → rate limit → cookies →       │  │
│  │  MySQL-backed session → current user → language → static → │  │
│  │  view engine → layouts                                     │  │
│  └───────────────────────────┬───────────────────────────────┘  │
│                               ▼                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Routes → Controllers (business logic)                     │  │
│  └───────────────────────────┬───────────────────────────────┘  │
│                               ▼                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Models — raw parameterized SQL, no ORM                    │  │
│  └───────────────────────────┬───────────────────────────────┘  │
│                               ▼                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Error handler (winston) — last middleware, catches every  │  │
│  │  next(err) from every controller                           │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     MySQL 8 (mysql2 connection pool)              │
└─────────────────────────────────────────────────────────────────┘

Security

Design Patterns

Role-Based Access

RoleAccess
AdminEvery article across every organization (filter, block/unblock), organization management (create org + manager account atomically, block/unblock), category management, user list
ManagerTheir own organization's articles only (filter by author), approve/flag — enforced as a single atomic query against the org's own non-blocked status
JournalistTheir own articles only — create, list/filter; self-approve/flag only when not attached to an organization

Project Structure

src/
├── app.ts, index.ts        # App class: middleware + route wiring, entry point
├── common/                 # Constants, Translation, MySQL duplicate-key helper
├── config/                 # env loading, winston logger, session store
├── controllers/            # admin, manager, journalist, my-articles, articles,
│                            # articles-api, articles-rss, article-images, auth, pages
├── db/                     # mysql2 connection pool
├── middleware/              # requireAuth, requireRole, currentUser, language,
│                            # rateLimiter, multer, fakeHeaders, errorHandler
├── models/                  # articles, organizations, categories, users, languages
├── routes/
├── translations/            # langEn, langRo
├── types/
└── validation/               # Zod schemas + the parseAndValidate() helper

views/                       # EJS templates: admin/, manager/, journalist/, articles/, public/
scss/                        # Sass source → public/css/main.css
uploads/articles/            # uploaded images, gitignored

Sample Endpoints

MethodEndpointDescriptionAuth
GET/healthHealth check (DB connectivity)Public
GET/v1/feedPaginated public JSON feedPublic
GET/v1/feed.rssRSS 2.0, 50 most recent, 15-min cachePublic
GET/news/:idArticle detailPublic
GET/articles/:id/images/:filenameGated image servingPublic, per-article
POST/loginSession loginPublic, rate-limited
POST/registerJournalist self-registrationPublic, rate-limited
GET/articles/newNew article form (images, reference links)Authenticated
GET/admin/dashboardAll articles, filterable by organizationAdmin
GET/manager/dashboardOwn organization's articles, filterable by authorManager

Database Schema (Key Tables)

Session store and Prisma migration-bookkeeping tables omitted — this is the domain schema only.

┌───────────────┐     ┌──────────────────────┐     ┌────────────────────┐
│ organizations │     │        users         │     │      articles      │
├───────────────┤     ├──────────────────────┤     ├────────────────────┤
│ id (PK)       │     │ organization_id (FK) │     │ id (PK)            │
│ name (unique) │     │ role_id (FK)         │     │ author_id (FK)     │
│ blocked       │     │ email (unique)       │     │ author_org_id (FK) │
└───────────────┘     │ phone (unique)       │     │ source_org_id (FK) │
                      │ password_hash        │     │ category_id (FK)   │
                      │ api_key (unique)     │     │ language_id (FK)   │
                      └──────────────────────┘     │ status_id (FK)     │
                                                   │ admin_blocked      │
                                                   │ images (json)      │
                                                   │ reference_links    │
                                                   └────────────────────┘

┌───────────────────┐     ┌──────────────────────┐
│       roles       │     │   article_statuses   │
├───────────────────┤     ├──────────────────────┤
│ admin / manager / │     │ pending / approved / │
│ journalist        │     │ flagged              │
└───────────────────┘     └──────────────────────┘

┌──────────────────┐     ┌───────────────┐
│    categories    │     │   languages   │
├──────────────────┤     ├───────────────┤
│ id (PK)          │     │ id (PK)       │
│ slug (unique)    │     │ code (unique) │
│ parent_id (self) │     │ name          │
└──────────────────┘     └───────────────┘

Resources