feat: add dual-container compose configuration and test environment specifications
This commit is contained in:
parent
5f71688f20
commit
0ff507f67d
3 changed files with 149 additions and 14 deletions
66
docker-compose.yml
Normal file
66
docker-compose.yml
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Production Application Instance
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
app-prod:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: special-hotel-prod
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3000:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
|
||||||
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||||
|
- N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL}
|
||||||
|
- N8N_WEBHOOK_SECRET=${N8N_WEBHOOK_SECRET}
|
||||||
|
# Log tag prefix for syslog / journald, or environment labeling for standard output
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
tag: "app-prod/{{.Name}}"
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Development & Testing Application Instance
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
app-dev:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: special-hotel-dev
|
||||||
|
restart: no # Do not auto-restart if variables are missing or if it gracefully stops
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:3001:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- DATABASE_URL=${DATABASE_URL_DEV}
|
||||||
|
- TEST_DATABASE_URL=${TEST_DATABASE_URL}
|
||||||
|
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL_DEV}
|
||||||
|
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET_DEV}
|
||||||
|
- N8N_TEST_WEBHOOK_URL=${N8N_TEST_WEBHOOK_URL}
|
||||||
|
- N8N_WEBHOOK_SECRET=${N8N_WEBHOOK_SECRET_DEV}
|
||||||
|
# Commands run a pre-startup verification. If development-exclusive
|
||||||
|
# variables are missing, it logs a warning and exits with code 0 (graceful exit).
|
||||||
|
entrypoint: >
|
||||||
|
/bin/sh -c "
|
||||||
|
if [ -z \"$${TEST_DATABASE_URL}\" ] || [ -z \"$${N8N_TEST_WEBHOOK_URL}\" ]; then
|
||||||
|
echo '[DEV] Required development-exclusive variables (TEST_DATABASE_URL, N8N_TEST_WEBHOOK_URL) are missing.';
|
||||||
|
echo '[DEV] Gracefully shutting down the development service with exit code 0.';
|
||||||
|
exit 0;
|
||||||
|
fi;
|
||||||
|
echo '[DEV] Starting Next.js development server...';
|
||||||
|
exec npm run dev
|
||||||
|
"
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
tag: "app-dev/{{.Name}}"
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
@ -201,4 +201,34 @@ With `tea` configured, you can manage pull requests directly from the repository
|
||||||
* **Merge a PR**: `tea pulls merge <PR_NUMBER>`
|
* **Merge a PR**: `tea pulls merge <PR_NUMBER>`
|
||||||
* **Approve/Review a PR**: `tea pulls review <PR_NUMBER> --approve`
|
* **Approve/Review a PR**: `tea pulls review <PR_NUMBER> --approve`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Testing Procedures & Webhook Testing Flow
|
||||||
|
|
||||||
|
Testing must occur in complete isolation from production data. We achieve this by splitting execution paths using dedicated test hooks in both Next.js and n8n.
|
||||||
|
|
||||||
|
### 4.1. Development & Test Variables Configuration
|
||||||
|
During test executions (such as running Jest, Cypress, or integration suites), the application uses the following development-exclusive variables:
|
||||||
|
* `TEST_DATABASE_URL`: Dedicated database connection URL for test schemas (e.g. `postgresql://.../special_hotel_test`). Migrations are run here independently.
|
||||||
|
* `N8N_TEST_WEBHOOK_URL`: The specific path n8n exposes for test triggers.
|
||||||
|
|
||||||
|
### 4.2. n8n Testing Webhook Routing
|
||||||
|
All calculations triggered by test suites route to n8n via `/webhook-test` path segments:
|
||||||
|
1. **Trigger**: Test suite invokes n8n via `POST ${process.env.N8N_TEST_WEBHOOK_URL}/calculate-commissions`.
|
||||||
|
2. **n8n Path Branching**:
|
||||||
|
- Within the n8n canvas, an **`IF` node** checks: `{{ $json.headers["x-nginx-original-uri"] || $json.path }}` contains `webhook-test`.
|
||||||
|
- **True**: The n8n workspace connects to the database utilizing `TEST_DATABASE_URL` credentials. It pulls test sales/goals data and pushes calculations back to the Next.js dev API.
|
||||||
|
- **False**: Connects to the main `DATABASE_URL` for production processing.
|
||||||
|
|
||||||
|
### 4.3. Running Integration Tests Locally
|
||||||
|
To run the full sandbox locally:
|
||||||
|
1. Spin up both databases: `docker compose up -d postgres-dev postgres-test`.
|
||||||
|
2. Run database migrations on the test database:
|
||||||
|
```bash
|
||||||
|
DATABASE_URL=$TEST_DATABASE_URL npx prisma migrate deploy
|
||||||
|
```
|
||||||
|
3. Run the Next.js development server (which connects to the dev database by default, but switches API routing to test endpoints under integration scripts).
|
||||||
|
4. Run testing script: `npm run test:integration`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,23 +18,30 @@
|
||||||
|
|
||||||
## 2. Decoupled Network & Environment Independence
|
## 2. Decoupled Network & Environment Independence
|
||||||
|
|
||||||
The project is structured to run in any isolated Docker environment. All parameters are fed via environment variables:
|
The project is structured to run in any isolated Docker environment. All parameters are fed via environment variables to cleanly partition production and development configurations:
|
||||||
|
|
||||||
* `DATABASE_URL`: Connection string for PostgreSQL (e.g. `postgresql://user:pass@host:port/dbname`).
|
### 2.1. Environment Variables
|
||||||
* `N8N_WEBHOOK_URL`: The entry point for the n8n workflow engine.
|
* **Core Variables (Both Env)**:
|
||||||
* `N8N_API_KEY`: Token to authorize callbacks from n8n to the Next.js API.
|
* `DATABASE_URL`: Connection string for the active database (Production or Development).
|
||||||
* `NEXTAUTH_SECRET`: Secret for securing JWT cookies.
|
* `NEXT_PUBLIC_APP_URL`: The domain or local host path of the running application.
|
||||||
|
* `NEXTAUTH_SECRET`: Secret key for JWT session validation.
|
||||||
|
* **Development-Exclusive Test Variables**:
|
||||||
|
* `TEST_DATABASE_URL`: Connection string to the secondary sandbox/test database.
|
||||||
|
* `N8N_TEST_WEBHOOK_URL`: The n8n testing webhook entry point. Used by development services and test runners.
|
||||||
|
* `N8N_WEBHOOK_SECRET`: Token to authorize and verify n8n webhook payload signatures locally.
|
||||||
|
|
||||||
### Component Interaction:
|
### 2.2. Component Interaction:
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
User([User Client]) -->|HTTPS| WebApp[Next.js App Router]
|
User([User Client]) -->|HTTPS| WebApp[Next.js App Router]
|
||||||
|
|
||||||
subgraph Isolated Network
|
subgraph Isolated Network
|
||||||
WebApp -->|Prisma Client| DB[(PostgreSQL)]
|
WebApp -->|Prisma Client| DB[(PostgreSQL Main)]
|
||||||
WebApp -->|HTTP POST Webhook| n8n[n8n Workflow Engine]
|
WebApp -.->|Prisma Client - Test Env| DBTest[(PostgreSQL Test)]
|
||||||
|
WebApp -->|HTTP POST Webhook /webhook-test| n8n[n8n Workflow Engine]
|
||||||
|
n8n -->|IF Webhook Path Match| DBTest
|
||||||
|
n8n -->|Else| DB
|
||||||
n8n -->|HTTP POST Callback| WebApp
|
n8n -->|HTTP POST Callback| WebApp
|
||||||
n8n -->|Interact| LLM[AI Model / Provider]
|
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -190,11 +197,21 @@ By offloading calculations and integrations to n8n, we achieve a highly visual,
|
||||||
### 4.2. Settlement Calculation Workflow
|
### 4.2. Settlement Calculation Workflow
|
||||||
1. **Trigger**: Next.js calls n8n to execute the calculation for period `YYYY-MM`.
|
1. **Trigger**: Next.js calls n8n to execute the calculation for period `YYYY-MM`.
|
||||||
2. **n8n Processing**:
|
2. **n8n Processing**:
|
||||||
- Pulls active plans, individual goals, and actual sales from the Next.js API.
|
- Pulls active plans, individual goals, and actual sales from the Next.js API.
|
||||||
- Evaluates the mathematical formulas.
|
- Evaluates the mathematical formulas.
|
||||||
- Evaluates rule scales and applies caps.
|
- Evaluates rule scales and applies caps.
|
||||||
- Generates notifications (via email or push notifications) using n8n integrations.
|
- Generates notifications (via email or push notifications) using n8n integrations.
|
||||||
3. **Response**: Updates database records via the Next.js API and completes the task.
|
3. **Response**: Updates database records via the Next.js API and completes the task.
|
||||||
|
|
||||||
|
### 4.3. Test Webhook Branching & Database Isolation in n8n
|
||||||
|
To ensure complete isolation of production data, all n8n workflows must follow a strict testing branch architecture:
|
||||||
|
1. **Webhook Entry Node**: n8n listens on two webhook path variants:
|
||||||
|
- Production calls hit: `/webhook/calculate-commissions`
|
||||||
|
- Test suite calls hit: `/webhook-test/calculate-commissions`
|
||||||
|
2. **Conditional Path Routing**:
|
||||||
|
- An `IF` node immediately checks if the webhook request path contains `webhook-test`.
|
||||||
|
- **True (Test Mode)**: The workflow overrides its database credential node configurations to connect to `TEST_DATABASE_URL` (the secondary test sandbox database) and makes API callbacks back to the Next.js test instance.
|
||||||
|
- **False (Prod Mode)**: The workflow executes against the main `DATABASE_URL` and interacts with the production Next.js instance.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -211,3 +228,25 @@ We define role-based access restrictions as follows:
|
||||||
| **Analista Financiero** | Reviews calculations. Exports consolidated PDF/Excel reports. | Cannot approve. |
|
| **Analista Financiero** | Reviews calculations. Exports consolidated PDF/Excel reports. | Cannot approve. |
|
||||||
| **Consulta** | Read-only. | No mutations allowed. |
|
| **Consulta** | Read-only. | No mutations allowed. |
|
||||||
| **Colaborador** | Consults own history & dashboard. | Restricted to `user_id`. |
|
| **Colaborador** | Consults own history & dashboard. | Restricted to `user_id`. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Dual-Environment Docker Deployment Model
|
||||||
|
|
||||||
|
We design the containerized architecture to run two distinct instances of the application concurrently from the same codebase, ensuring clean division.
|
||||||
|
|
||||||
|
### 6.1. Service Configurations
|
||||||
|
* **Production Container (`app-prod`)**:
|
||||||
|
* **Network Port**: Exposed on port `3000` (mapped via Caddy to `special-hotel.yourdomain.com`).
|
||||||
|
* **Database**: Bound to the production `DATABASE_URL`.
|
||||||
|
* **Logs**: Prefixed with `[PROD]` inside the container engine.
|
||||||
|
* **Development Container (`app-dev`)**:
|
||||||
|
* **Network Port**: Exposed on port `3001` (mapped via Caddy to `special-hotel-dev.yourdomain.com`).
|
||||||
|
* **Database**: Bound to `TEST_DATABASE_URL` (acting as its main `DATABASE_URL` for test isolated migrations).
|
||||||
|
* **Logs**: Prefixed with `[DEV]` for easy debugging contrast.
|
||||||
|
|
||||||
|
### 6.2. Graceful Dev Failure Model
|
||||||
|
The development instance utilizes a validation startup hook. If any development-exclusive environment variables (like `TEST_DATABASE_URL`) are omitted:
|
||||||
|
1. The `app-dev` container logs a clear notification: `[DEV] Missing required development variables. Gracefully shutting down development service.`
|
||||||
|
2. The entrypoint script exits with **exit code 0**.
|
||||||
|
3. Docker or the compose orchestrator registers the container as cleanly stopped (not crashed). The production stack is completely unaffected, avoiding restart-loop penalties or deployment failures.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue