Project Structure

The eTeamups Platform is a TypeScript monorepo that contains multiple backend microservices, shared libraries, email templates, infrastructure configuration, and operational scripts in a single repository.

Root Directory Layout

eteamups-platform/
  services/            # Microservice source code
  libs/                # Shared libraries used by all services
  emails/              # Email templates (React Email, MJML, compiled HTML)
  nginx/               # Nginx reverse proxy configuration
  docker/              # Dockerfiles for containerised builds
  scripts/             # Operational and deployment shell scripts
  swagger/             # Generated OpenAPI specifications
  docs/                # Project documentation
  ssl/                 # Generated SSL certificates (git-ignored)
  logs/                # Runtime log files (git-ignored)
  dist/                # Compiled JavaScript output (git-ignored)
  package.json         # Root package manifest with all npm scripts
  tsconfig.json        # TypeScript compiler configuration
  docker-compose.local.yml   # Docker Compose for local development
  docker.local.env     # Reference environment variables for local dev
  instrumentation.ts   # OpenTelemetry instrumentation bootstrap

services/ – Microservices

Each directory under services/ is a self-contained microservice. Every service follows the same internal structure.

Service List

Directory Service Port Description
services/auth/ Auth 9000 Authentication: login, OTP verification, token management
services/profile/ Profile 9100 User profiles, teams, and teammates
services/organisation/ Organisation 9107 Organisations, accounts, reference data (countries, industries, skills, etc.)
services/media/ Media 9102 Media/file handling
services/messaging/ Messaging Email and SMS client classes (used by the message queue worker)
services/common/ Common Shared middleware (BasicMiddleware) used across all services
services/store/ Store Mongoose models, schemas, DTOs, and seed data (data layer)

Internal Service Layout

Each runnable service (auth, profile, organisation, media) follows this pattern:

services/{name}/
  src/
    config/
      server.config.dev.ts     # Development configuration
      server.config.prod.ts    # Production configuration
    middleware/
      {Feature}Middleware.ts   # Route-mounting middleware classes
    modules/
      routes/
        {feature}/
          {action}.ts          # Individual route handlers
      error_messages/
        {feature}.ts           # Centralised error message constants
      utils/
        Assert.ts              # Assertion helpers for request validation
    index.ts                   # (optional) barrel exports

Example – Auth service modules:

services/auth/src/modules/routes/
  login/login.ts       # POST login (request OTP)
  verify/verify.ts     # POST verify OTP
  token/token.ts       # Token refresh / management
  version/version.ts   # Version endpoint

Example – Organisation service modules:

services/organisation/src/modules/routes/
  account/             # CRUD for accounts
  country/             # CRUD for countries
  educationLevel/      # CRUD for education levels
  function/            # CRUD for job functions
  industry/            # CRUD for industries
  location/            # CRUD for locations
  organisation/        # CRUD for organisations
  skill/               # CRUD for skills
  study-major/         # CRUD for study majors

Config-Driven Server Startup

Services do not have their own main() entry point. Instead, startup is handled by the shared server framework:

  1. libs/server/src/run.ts reads a config file path from CLI arguments.
  2. The config file is loaded via ConfigFileResolver, which supports .ts, .js, and .json files.
  3. The config file exports an IServerConfig object:
// IServerConfig interface
interface IServerConfig {
  processName: string;       // Display name shown in the startup banner
  port: number;              // TCP port the service listens on
  baseUrl: string;           // Base URL for the service
  logLevel?: "debug" | "info";
  middlewares?: IMiddleware[];// Middleware classes to auto-load
}

interface IMiddleware {
  path: string;              // Relative path from project root to middleware file
}
  1. A Server instance is created with the config. It sets up Express with rate limiting, JSON body parsing, URL encoding, health endpoints (/_ping and /health), and then dynamically imports and instantiates each middleware class listed in the config.

Example config (services/auth/src/config/server.config.dev.ts):

const config: IServerConfig = {
  processName: "Auth Server",
  port: 9000,
  baseUrl: "http://localhost",
  logLevel: "debug",
  middlewares: [
    { path: "services/auth/src/middleware/AuthMiddleware.ts" },
    { path: "services/auth/src/middleware/TokenMiddleware.ts" },
    { path: "services/auth/src/middleware/VerifyMiddleware.ts" },
  ]
};

Each middleware extends BasicMiddleware (from services/common/) and registers Express routes on the app instance in its constructor.

The Store – Data Access Layer

services/store/ is not a runnable service. It is the shared data layer used by all services.

services/store/src/
  dto/                # TypeScript interfaces for each entity
    IAccount.ts
    IOtp.ts
    ITeam.ts
    ...
  schema/             # Mongoose schema definitions
    AccountSchema.ts
    OtpSchema.ts
    TeamSchema.ts
    ...
  model/              # Model classes extending BaseModel
    AccountModel.ts
    OtpModel.ts
    TeamModel.ts
    BaseModel.ts      # Abstract base class
    ...
  seed/               # Seed data files (.js)
    accounts.js
    countries.js
    industries.js
    skills.js
    ...

BaseModel Pattern

All model classes extend the generic BaseModel<T> abstract class, which wraps common Mongoose operations:

Method Description
create(payload) Insert a new document with auto-generated UUID id and timestamps
findOne(filter) Find a single document matching the filter
findMany(filter) Find multiple documents, sorted by createdAt descending
delete(filter) Delete all documents matching the filter
patch(filter, payload) Update all documents matching the filter

The BaseModel constructor automatically triggers the MongoDB connection singleton (MongooseClient.getInstance()) so that any model usage ensures a live database connection.

libs/ – Shared Libraries

Shared code is organised into focused libraries under libs/.

Directory Purpose
libs/server/ Express server framework wrapper (Server class, run.ts entry point, IServerConfig interface, OpenTelemetry tracer setup)
libs/mongo/ MongoDB connection singleton (MongooseClient) and a lower-level MongoClient wrapper
libs/utility/ Cross-cutting utilities: Logger (Winston + OpenTelemetry transport), SchemaValidation (Zod-based Express middleware), EmailUtility (HTML template compiler), ConfigFileResolver (JSON/TS/JS config loader), time helpers
libs/message-queue/ BullMQ queue singleton (MessageQueue) and its worker runner (run.ts)
libs/seeder/ Database seeder that reads .js files from a directory and bulk-inserts them into MongoDB collections

Logger

The Logger class (libs/utility/src/Logger.ts) wraps Winston and provides static methods: log, info, error, debug, warn. Logs are written to:

  • Console (with colour and timestamp formatting)
  • error.log (error-level entries only)
  • combined.log (all levels)
  • OpenTelemetry transport (for forwarding to SigNoz or other OTLP backends)

The log level is controlled by the LOG_LEVEL environment variable (defaults to info).

MessageQueue

The MessageQueue class (libs/message-queue/MessageQueue.ts) is a singleton that wraps a BullMQ Queue named "email-queue". It connects to Redis using REDIS_HOST and REDIS_PORT from the environment. It provides addJob and getJob methods. The worker process is started separately via npm run message-queue.

emails/ – Email Templates

emails/
  admin_otp.tsx       # React Email component for OTP emails
  html/               # Compiled HTML templates (used at runtime)
  mjml/               # MJML source templates (compiled to HTML)

The platform uses three email authoring approaches:

  1. React Email (.tsx files) – Component-based email templates for the development workflow. Run npm run email:dev to start a preview server on port 3010.
  2. MJML – Responsive email markup compiled to HTML.
  3. Compiled HTML – Final templates in emails/html/ are used at runtime by EmailUtility.compile(), which performs {{variable}} substitution.

nginx/ – Reverse Proxy

nginx/
  nginx.conf           # Production Nginx main config
  nginx.local.conf     # Local development Nginx main config
  conf.d/
    local.conf         # Local server blocks (HTTP redirect + HTTPS proxy)
    eteamups.conf      # Production server blocks

The local Nginx configuration (conf.d/local.conf) acts as an API gateway:

Path Proxied To
/auth/* http://host.docker.internal:9000
/profile/* http://host.docker.internal:9100
/organisation/* http://host.docker.internal:9107
/media/* http://host.docker.internal:9102
/health Returns 200 "healthy"
/ Returns JSON listing all service URLs

Since Nginx runs in a Docker container but the services run on the host, host.docker.internal is used to route traffic from the container to the host machine.

docker/ – Dockerfiles

docker/
  Dockerfile.auth           # Auth service image
  Dockerfile.profile        # Profile service image
  Dockerfile.organisation   # Organisation service image
  Dockerfile.media          # Media service image
  Dockerfile.message-queue  # Message queue worker image
  Dockerfile.nginx          # Nginx reverse proxy image

Each service Dockerfile compiles TypeScript to JavaScript and runs the production entry point (e.g., npm run auth:prod). Production builds use NODE_OPTIONS=--max-old-space-size=512 and -r ./dist/instrumentation.js for OpenTelemetry auto-instrumentation.

scripts/ – Operational Scripts

The scripts/ directory contains shell scripts for development, deployment, and operations:

Script Purpose
generate-ssl.sh Generate self-signed SSL certificates for local development
start.sh / stop.sh Start/stop services
start-local.sh / stop-local.sh Local development start/stop
docker-start.sh / docker-stop.sh Docker container management
docker-build.sh / docker-push.sh Build and push Docker images
docker-build-push.sh Combined build + push
buildx-multiarch.sh Multi-architecture Docker builds
docker-container-refresh.sh Refresh running containers
docker-nginx-build-push.sh Build and push the Nginx image
seed.sh / seed-docker.sh Database seeding (local and Docker)
setup-env.sh Environment setup
kill-ports.sh Kill processes on service ports
emails.sh Email-related operations
backup.sh / restore.sh Database backup and restore
health-check.sh Service health verification
log-monitor.sh / view-logs.sh Log monitoring and viewing
rotate-logs.sh / setup-log-rotation.sh Log rotation configuration
setup-microservice-logging.sh Per-service logging setup
setup-lets-encrypt.sh Let’s Encrypt certificate provisioning
start-production.sh Production deployment start

swagger/ – OpenAPI Specifications

Generated OpenAPI specifications are stored in swagger/:

swagger/
  zeswa-hub-open-api.json
  zeswa-hub-open-api.yaml

Regenerate them with:

npm run swagger:generate

This runs scripts/generate-swagger.ts, which introspects the running service routes and produces the spec.