# VIETPRODEV Backend Guidelines

Code standards, conventions, and best practices for all VIETPRODEV backend projects.
Applies to: `sso-vietprodev-backend` and all future projects based on this template.

---

## Core Stack

| Layer | Technology | Notes |
|-------|-----------|-------|
| Runtime | Node.js 20+ | TypeScript, ESM via NodeNext |
| Framework | Express.js | With `express-automatic-routes` |
| ORM | Sequelize | PostgreSQL dialect |
| Auth (Core SSO) | `oidc-provider` | OAuth2/OIDC authorization server |
| Auth (Internal) | JWT (HS256/HS512) | bcryptjs for password hashing |
| Validation | Zod (config) + Joi (request) | Layered validation |
| Audit Logging | MongoDB | AuditRepository + AuditLoggerService |
| Caching | Redis | Graceful fallback if unavailable |
| API Docs | OpenAPI 3.0 | Zod schemas + `zod-to-openapi` |
| Testing | Swagger UI OAuth2 + Postman | See Phase 6 |

---

## 1. Configuration

All runtime config flows through **Zod schemas** — never read `process.env` directly in application code.

### Layered Config Loading

```
.env → env-schema.ts → schema.ts → [base | development | staging | production].ts → ConfigSchema.parse()
```

- **Never** hardcode values that belong in `.env`.
- **Never** skip the Zod schema validation.
- **Never** put production secrets in `.env.example`.
- Use `.env.local` for local overrides (gitignored).

### Dev Secrets Auto-Generation

In `src/index.ts`, dev secrets are auto-generated if missing/too-short. This ensures `pnpm run dev` works out of the box without a `.env` file. Production/staging always fail-fast if secrets are missing.

### Validation

- Zod schemas in `src/config/env-schema.ts` — env variable types and constraints.
- Joi in `src/middlewares/validators/` — request body/params/query validation.
- Never trust external input. Validate at every boundary.

---

## 2. Module Resolution

Use **path aliases** for all internal imports:

```typescript
import Config from '#config';
import { AuditLoggerService } from '#audit/auditLoggerService';
import { UserService } from '#services/userService';
```

**Never** use relative imports for cross-folder dependencies (e.g., `../../config`).

NodeNext resolution is enabled. Ensure every directory that is a module has an `index.ts`.

---

## 3. OIDC / SSO Core

The OIDC provider (`oidc-provider`) handles OAuth2/OIDC flows for the SSO authorization server.

### Provider Initialization

```typescript
// Initialize in server startup — BEFORE listening
await OidcService.initialize();
```

### Session Management

OIDC interactions (login, consent) use **Handlebars templates** in `src/oidc/views/`. The provider is configured with custom interaction URLs pointing to Express routes in `oidcInteractionsController.ts`.

### OIDC Route Rules

- OIDC routes **must** be mounted **before** `express-automatic-routes` in `server.ts`.
- OIDC routes use **lazy middleware** (not module-level function calls) to avoid initialization order issues.
- All OIDC interactions are audited via `AuditLoggerService`.

### User Resolution

`findAccount` in the Provider config resolves `sub` (subject) to account claims. In production, this should look up the user in the database.

---

## 4. Audit Logging

All significant events must be audited.

### Usage

```typescript
import { AuditLoggerService } from '#audit/auditLoggerService';
import { AUDIT_EVENTS } from '#audit/auditEvents';

const auditLog = new AuditLoggerService(/* strategy */);
auditLog.log(AUDIT_EVENTS.AUTH_LOGIN, { userId, ip, userAgent, status: 'success' });
```

### Event Types

Define in `src/audit/auditEvents.ts`. Follow the existing `AUDIT_EVENTS` constants pattern.

### MongoDB Fallback

MongoDB is used for audit logs. If MongoDB is unavailable, audit entries are queued in memory with retry (exponential backoff).

---

## 5. Database

### Sequelize Conventions

- Models go in `src/models/`.
- Migrations go in `sql/migrations/` (numbered, SQL-based).
- Use `QueryTypes` from sequelize for raw queries (e.g., OIDC adapter).
- Always handle `rows` as potentially undefined.

### Multi-Pool

For read/write splitting, use `MultiPoolService`:

```typescript
const pool = MultiPoolService.getPool('default');
const results = await pool.query('SELECT * FROM users', { type: QueryTypes.SELECT });
```

### Partition Management

Partitioned tables (auth_audit_logs, refresh_token_audit_logs, notification_delivery_attempts) are auto-created on startup. Failures are non-blocking.

---

## 6. Error Handling

- All errors should have a user-safe message and a technical log (English).
- Use `AppError` or similar custom error class for business logic errors.
- Never expose stack traces or internal paths to API consumers.
- Always return consistent error response shapes:

```json
{ "error": "MESSAGE_KEY", "message": "Human-readable message", "details": {} }
```

---

## 7. Naming Conventions

| Type | Convention | Example |
|------|-----------|---------|
| Files | kebab-case | `oidc-adapter.service.ts` |
| Classes | PascalCase | `OidcService` |
| Functions/methods | camelCase | `getInstance` |
| Constants | SCREAMING_SNAKE | `MAX_RETRY_COUNT` |
| Enums | PascalCase + SCREAMING_SNAKE members | `AuditEventType.LOGIN` |
| Environment vars | SCREAMING_SNAKE | `JWT_SECRET` |
| Database tables | snake_case | `oidc_grants` |
| Database columns | snake_case | `created_at` |

---

## 8. Git Workflow

### Branch Naming

```
<type>(<scope>): <subject>

feat(auth): add OIDC refresh token rotation
fix(oidc): resolve provider initialization order
chore(deps): upgrade oidc-provider to v9.8.4
docs(readme): update OIDC endpoint table
```

### Types

- `feat` — new feature
- `fix` — bug fix
- `chore` — tooling, deps, config
- `docs` — documentation only
- `refactor` — code restructuring without behavior change
- `perf` — performance improvement
- `test` — adding tests
- `ci` — CI/CD changes

### Commit Rules

- Subject line: max 72 characters, imperative mood.
- Body: explain **what** and **why**, not how.
- Reference issues: `Closes #123`, `Refs #456`.
- Never commit secrets or `.env` files.

---

## 9. Security Rules

- **Never** log secrets, passwords, or tokens.
- **Never** commit `.env` files — always `.env.example` with placeholder values.
- Validate all input at API boundaries (Joi).
- Use parameterized queries for raw SQL (Sequelize `replacements`).
- Production JWT/encryption secrets must be >= 32 characters.
- OIDC issuer URL must match the actual deployment URL (including protocol and port).
- Enable CORS only for trusted origins.
- Use Helmet CSP headers — update for Handlebars inline styles.

---

## 10. File Organization

```
src/
├── audit/          # Audit logging (MongoDB-based)
├── config/         # Zod schemas & config loaders
├── constants/      # App-wide constants
├── controllers/    # express-automatic-routes controllers
├── database/
│   ├── mongo/     # MongoDB client & collections
│   └── sequelize/ # Sequelize setup & migrations
├── interfaces/     # Shared TypeScript interfaces
├── middleware/    # Express middleware (auth, validation, etc.)
├── models/        # Sequelize models
├── oidc/          # OIDC provider & Handlebars views
│   └── views/     # login.hbs, register.hbs, consent.hbs
├── routes/        # express-automatic-routes (auto-discovered)
├── services/      # Business logic (auth, database, storage, etc.)
├── utils/         # Pure utility functions
└── validators/    # Request validators (Joi schemas)
```

---

## 11. Testing & Demo

### Swagger UI (OAuth2)

- All API endpoints documented at `/swagger/index`.
- OIDC flows are testable through interactive forms.
- See `guidelines/AUTHENTICATION_GUIDE.md` for Swagger OAuth2 setup.

### Postman

- Import `postman/sso-vietprodev-backend.postman_collection.json`.
- Pre-configured requests for all API and OIDC flows.
- Environment variables for easy switching between local/staging/production.

### Local Demo

```bash
# 1. Start infra
docker compose up -d postgres redis mongo

# 2. Run migrations
pnpm db:migrate

# 3. Start server
pnpm run dev

# 4. Open Swagger UI
# http://localhost:3001/swagger/index

# 5. Register a test user via the registration endpoint or OIDC interaction
```

---

## 12. Scripts Reference

| Command | Purpose |
|---------|---------|
| `pnpm run dev` | Hot-reload dev server |
| `pnpm run build` | Production build |
| `pnpm run start` | Run production build |
| `pnpm tsc` | Type-check only |
| `pnpm lint` | Lint check |
| `pnpm lint:fix` | Auto-fix lint |
| `pnpm db:migrate` | Run migrations |
| `pnpm db:migrate:rollback` | Rollback last |
| `pnpm db:seed` | Seed data |
| `pnpm gen-api <model>` | Scaffold CRUD API |

---

## Quick Reference: Common Patterns

### Adding a New Service

1. Create file in `src/services/<domain>/`.
2. Register as singleton if stateful (e.g., `MultiPoolService` pattern).
3. Add unit tests in `tests/services/`.
4. Document the service interface.

### Adding a New API Route

```bash
pnpm gen-api MyResource
```

Manual: Create `src/routes/MyResource.ts` with express route definitions. The `express-automatic-routes` system auto-discovers it.

### Adding an Audit Event

1. Add constant to `src/audit/auditEvents.ts` (PascalCase).
2. Use it everywhere: `auditLogger.log(AUDIT_EVENTS.MY_EVENT, { ... })`.

### Adding an OIDC Client

1. Register in `src/oidc/oidcAdapterService.ts` (clients table via migration).
2. Configure redirect URIs and scopes.
3. Document in Postman collection.

---

## Related Documents

- `guidelines/CODING_CONVENTION.md` — Detailed coding standards
- `guidelines/AUTHENTICATION_GUIDE.md` — Auth flow details
- `guidelines/API_GENERATOR_GUIDE.md` — API scaffolding
- `guidelines/VALIDATION_GUIDE.md` — Joi validation patterns
- `docs/backend/commit.md` — Commit message format
- `RUN.md` — Local development setup
