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:
libs/server/src/run.tsreads a config file path from CLI arguments.- The config file is loaded via
ConfigFileResolver, which supports.ts,.js, and.jsonfiles. - The config file exports an
IServerConfigobject:
// 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
}
- A
Serverinstance is created with the config. It sets up Express with rate limiting, JSON body parsing, URL encoding, health endpoints (/_pingand/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:
- React Email (
.tsxfiles) – Component-based email templates for the development workflow. Runnpm run email:devto start a preview server on port 3010. - MJML – Responsive email markup compiled to HTML.
- Compiled HTML – Final templates in
emails/html/are used at runtime byEmailUtility.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.