chore(monorepo): scaffold npm workspaces and tooling

This commit is contained in:
lakshit verma 2026-08-17 01:31:48 +05:30
commit 99458eb562
No known key found for this signature in database
8 changed files with 9397 additions and 0 deletions

83
.env.example Normal file
View file

@ -0,0 +1,83 @@
# ─────────────────────────────────────────────────────────────────────────────
# Field Service Work Order Management — environment template
#
# Copy to `.env` and fill in real values:
# cp .env.example .env
# NEVER commit `.env`. Real secrets must never be committed to the repo.
# ─────────────────────────────────────────────────────────────────────────────
# ── HTTP / runtime ────────────────────────────────────────────────────────────
# Port the Express API listens on (default 4000).
PORT=4000
# development | test | production
NODE_ENV=development
# ── Database ──────────────────────────────────────────────────────────────────
# MongoDB connection string (required).
MONGODB_URI=mongodb://127.0.0.1:27017/workorders
# ── Secrets (REQUIRED) ────────────────────────────────────────────────────────
# HS256 signing secret for the 15-minute access JWT.
# MUST be >= 32 characters and MUST NOT be a placeholder such as "secret",
# "changeme", or "replace-me". Rotating it invalidates issued access tokens.
JWT_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef
# Secret used by cookie-parser to sign cookie values (integrity only).
# MUST be >= 32 characters. It is NOT authentication.
COOKIE_SECRET=fedcba9876543210fedcba9876543210fedcba9876543210
# NOTE: JWT_SECRET and COOKIE_SECRET must each be at least 32 characters and
# must not be placeholder values. Generate real ones with:
# openssl rand -hex 32
# and never commit them to version control.
# ── CORS / URLs ───────────────────────────────────────────────────────────────
# Comma-separated list of allowed origins. `credentials: true` is set, so "*"
# is rejected. Same-origin deployments only need the SPA origin.
CORS_ORIGIN=http://localhost:5173
# Public SPA origin (used for password-reset links and cookie handling).
APP_URL=http://localhost:5173
# ── Mailer ────────────────────────────────────────────────────────────────────
# Resend API key (https://resend.com/docs/api-reference/emails/send).
# Leave empty to use the log-only mailer (logs a REDACTED reset URL).
RESEND_API_KEY=
# Verified sender. Defaults to Resend's shared onboarding@resend.dev
# (only delivers to the account owner until you verify your own domain).
RESEND_FROM=
SMTP_URL=smtp://user:pass@localhost:1025
# ── Proxy / observability ─────────────────────────────────────────────────────
# Number of trusted reverse-proxy hops. nginx sets X-Forwarded-* in prod;
# keep 0 when the API is reached directly (local dev).
TRUST_PROXY_HOPS=0
# Set to true to return stack traces in 500 responses (dev only, never prod).
DEBUG_ERRORS=false
# pino log level: trace | debug | info | warn | error | fatal
LOG_LEVEL=info
# ── Rate limiting (window in ms, max requests per key) ────────────────────────
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=300
RATE_LIMIT_LOGIN_MAX=10
RATE_LIMIT_FORGOT_MAX=3
# ── Seed credentials (`npm run seed`, idempotent) ─────────────────────────────
SEED_ADMIN_EMAIL=admin@example.com
SEED_ADMIN_PASSWORD=Admin1234
SEED_USER_EMAIL=user@example.com
SEED_USER_PASSWORD=User1234
SEED_VIEWER_EMAIL=viewer@example.com
SEED_VIEWER_PASSWORD=Viewer1234
# ── Frontend (Vite, consumed at build time) ───────────────────────────────────
# Must match APP_URL.
VITE_APP_URL=http://localhost:5173
# Empty in dev → relative `/api/v1`, proxied by Vite (dev) or nginx (prod).
# Set to the API origin only for a split-origin deployment.
VITE_API_URL=

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
node_modules/
dist/
coverage/
*.log
.env
.env.*
!.env.example
.DS_Store
yarn.lock
pnpm-lock.yaml
.vscode/
.idea/
.vercel

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
20

15
eslint.config.js Normal file
View file

@ -0,0 +1,15 @@
const js = require('@eslint/js');
const tseslint = require('typescript-eslint');
const prettier = require('eslint-config-prettier');
module.exports = tseslint.config(
{ ignores: ['**/dist/**', '**/node_modules/**', '**/coverage/**'] },
js.configs.recommended,
...tseslint.configs.recommended,
prettier,
{
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
},
},
);

9225
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

35
package.json Normal file
View file

@ -0,0 +1,35 @@
{
"name": "mern-starter-app",
"version": "1.2.0",
"private": true,
"description": "Field Service Work Order Management — production-oriented MERN monorepo",
"workspaces": [
"packages/shared",
"backend",
"frontend"
],
"engines": {
"node": ">=20"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.3",
"typescript-eslint": "^8.3.0"
},
"scripts": {
"build": "npm run build -w @workorders/shared && npm run build -w backend && npm run build -w frontend",
"dev": "npm run dev -w backend & npm run dev -w frontend & wait",
"lint": "npm run lint -w @workorders/shared && npm run lint -w backend && npm run lint -w frontend",
"typecheck": "npm run typecheck -w @workorders/shared && npm run typecheck -w backend && npm run typecheck -w frontend",
"test": "npm run test -w @workorders/shared && npm run test -w backend && npm run test -w frontend",
"seed": "npm run seed -w backend"
},
"allowScripts": {
"mongodb-memory-server": true,
"esbuild@0.28.2": true,
"esbuild@0.21.5": true,
"express-rate-limit": true
}
}

8
prettier.config.js Normal file
View file

@ -0,0 +1,8 @@
/** @type {import('prettier').Config} */
module.exports = {
singleQuote: true,
trailingComma: 'all',
printWidth: 100,
semi: true,
tabWidth: 2,
};

17
tsconfig.base.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true,
"noUncheckedIndexedAccess": true,
"noFallthroughCasesInSwitch": true
}
}