wip
This commit is contained in:
62
.dockerignore
Normal file
62
.dockerignore
Normal file
@@ -0,0 +1,62 @@
|
||||
# Node modules
|
||||
node_modules
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
.next
|
||||
build
|
||||
out
|
||||
.turbo
|
||||
|
||||
# Env files (will be added separately)
|
||||
.env
|
||||
.env.*
|
||||
!.env.development
|
||||
!.env.production
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Tests
|
||||
coverage
|
||||
.nyc_output
|
||||
test-results
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs
|
||||
|
||||
# Large directories to exclude
|
||||
html-dumps
|
||||
html-dumps-optimized
|
||||
backups
|
||||
.husky
|
||||
|
||||
# Development files
|
||||
.prettierrc
|
||||
.eslintrc*
|
||||
tsconfig.tsbuildinfo
|
||||
44
.env.development
Normal file
44
.env.development
Normal file
@@ -0,0 +1,44 @@
|
||||
# ==========================================
|
||||
# GridPilot Development Environment
|
||||
# ==========================================
|
||||
|
||||
# Node Environment
|
||||
NODE_ENV=development
|
||||
|
||||
# ==========================================
|
||||
# Database (PostgreSQL)
|
||||
# ==========================================
|
||||
DATABASE_URL=postgres://gridpilot_user:gridpilot_dev_pass@db:5432/gridpilot_dev
|
||||
POSTGRES_DB=gridpilot_dev
|
||||
POSTGRES_USER=gridpilot_user
|
||||
POSTGRES_PASSWORD=gridpilot_dev_pass
|
||||
|
||||
# ==========================================
|
||||
# API Configuration
|
||||
# ==========================================
|
||||
API_PORT=3000
|
||||
API_HOST=0.0.0.0
|
||||
|
||||
# ==========================================
|
||||
# Website Configuration
|
||||
# ==========================================
|
||||
NEXT_PUBLIC_GRIDPILOT_MODE=alpha
|
||||
NEXT_PUBLIC_SITE_URL=http://localhost:3001
|
||||
NEXT_PUBLIC_API_URL=http://localhost:3000
|
||||
NEXT_PUBLIC_DISCORD_URL=https://discord.gg/your-invite-code
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# ==========================================
|
||||
# Vercel KV (Optional in Development)
|
||||
# ==========================================
|
||||
# KV_REST_API_URL=
|
||||
# KV_REST_API_TOKEN=
|
||||
|
||||
# ==========================================
|
||||
# Automation Mode
|
||||
# ==========================================
|
||||
AUTOMATION_MODE=dev
|
||||
CHROME_DEBUG_PORT=9222
|
||||
AUTOMATION_TIMEOUT=30000
|
||||
RETRY_ATTEMPTS=3
|
||||
SCREENSHOT_ON_ERROR=true
|
||||
53
.env.production
Normal file
53
.env.production
Normal file
@@ -0,0 +1,53 @@
|
||||
# ==========================================
|
||||
# GridPilot Production Environment
|
||||
# ==========================================
|
||||
|
||||
# Node Environment
|
||||
NODE_ENV=production
|
||||
|
||||
# ==========================================
|
||||
# Database (PostgreSQL)
|
||||
# ==========================================
|
||||
# IMPORTANT: Change these credentials in production!
|
||||
DATABASE_URL=postgres://gridpilot_user:CHANGE_ME_IN_PRODUCTION@db:5432/gridpilot_prod
|
||||
POSTGRES_DB=gridpilot_prod
|
||||
POSTGRES_USER=gridpilot_user
|
||||
POSTGRES_PASSWORD=CHANGE_ME_IN_PRODUCTION
|
||||
|
||||
# ==========================================
|
||||
# Redis Cache
|
||||
# ==========================================
|
||||
# IMPORTANT: Change password in production!
|
||||
REDIS_URL=redis://:CHANGE_ME_IN_PRODUCTION@redis:6379
|
||||
REDIS_PASSWORD=CHANGE_ME_IN_PRODUCTION
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
# ==========================================
|
||||
# API Configuration
|
||||
# ==========================================
|
||||
API_PORT=3000
|
||||
API_HOST=0.0.0.0
|
||||
|
||||
# ==========================================
|
||||
# Website Configuration
|
||||
# ==========================================
|
||||
NEXT_PUBLIC_GRIDPILOT_MODE=alpha
|
||||
NEXT_PUBLIC_SITE_URL=https://gridpilot.com
|
||||
NEXT_PUBLIC_API_URL=https://api.gridpilot.com
|
||||
NEXT_PUBLIC_DISCORD_URL=https://discord.gg/your-invite-code
|
||||
NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# ==========================================
|
||||
# Vercel KV (REQUIRED in Production)
|
||||
# ==========================================
|
||||
KV_REST_API_URL=your_kv_rest_api_url_here
|
||||
KV_REST_API_TOKEN=your_kv_rest_api_token_here
|
||||
|
||||
# ==========================================
|
||||
# Automation Mode
|
||||
# ==========================================
|
||||
AUTOMATION_MODE=production
|
||||
AUTOMATION_TIMEOUT=30000
|
||||
RETRY_ATTEMPTS=3
|
||||
SCREENSHOT_ON_ERROR=false
|
||||
@@ -1,62 +1,50 @@
|
||||
# 🏗 Architect
|
||||
|
||||
## Purpose
|
||||
Enforce **strict Clean Architecture, strict OOP, SOLID, KISS, and YAGNI**.
|
||||
Provide **direct architectural diagnosis and a concrete architectural plan**.
|
||||
|
||||
Nothing more.
|
||||
The Architect must ALWAYS output the **actual plan itself**,
|
||||
never a description of having created a plan.
|
||||
|
||||
---
|
||||
|
||||
## Core Rules (Non-Negotiable)
|
||||
## Absolute Rule: NO META OUTPUT
|
||||
The Architect MUST NOT:
|
||||
- describe work done
|
||||
- summarize that a plan exists
|
||||
- state readiness for implementation
|
||||
- talk about what Code mode should do
|
||||
- mention other modes
|
||||
- write “I have provided…”
|
||||
- write “This plan is ready…”
|
||||
- write “The following plan covers…”
|
||||
|
||||
### Clean Architecture
|
||||
- Domain contains business rules only.
|
||||
- Domain depends on nothing.
|
||||
- Application orchestrates, does not contain business rules.
|
||||
- Infrastructure contains details only.
|
||||
- UI contains no business rules.
|
||||
- Dependency direction is always inward.
|
||||
- No cross-layer imports.
|
||||
- Ports define boundaries, adapters implement them.
|
||||
- Presenters map data only, no logic.
|
||||
If the Architect has something to say,
|
||||
it MUST be said as **architecture content**, not commentary.
|
||||
|
||||
### OOP
|
||||
- One responsibility per class.
|
||||
- Behavior lives with data.
|
||||
- No anemic models.
|
||||
- No procedural code disguised as objects.
|
||||
- No god objects.
|
||||
- Prefer composition over inheritance.
|
||||
- Value Objects for invariants and meaning.
|
||||
---
|
||||
|
||||
### SOLID
|
||||
- Single responsibility.
|
||||
- Explicit dependencies.
|
||||
- Clear abstractions.
|
||||
- No hidden coupling.
|
||||
## Core Principles (Non-Negotiable)
|
||||
All architectural decisions MUST follow:
|
||||
- Clean Architecture (strict)
|
||||
- OOP
|
||||
- SOLID
|
||||
- KISS
|
||||
- YAGNI
|
||||
|
||||
### KISS
|
||||
- Simplest structure that works.
|
||||
- No cleverness.
|
||||
- No unnecessary abstraction.
|
||||
- No indirection without need.
|
||||
|
||||
### YAGNI
|
||||
- No features “for later”.
|
||||
- No abstractions “just in case”.
|
||||
- No structure without current demand.
|
||||
No exceptions unless the user explicitly overrides.
|
||||
|
||||
---
|
||||
|
||||
## Scope of Analysis
|
||||
The Architect analyzes **only the context provided by the Orchestrator**.
|
||||
The Architect analyzes ONLY:
|
||||
- context explicitly provided by the Orchestrator
|
||||
- files, modules, and goals explicitly named
|
||||
|
||||
The Architect does NOT:
|
||||
The Architect MUST NOT:
|
||||
- scan the repo
|
||||
- discover context
|
||||
- guess intent
|
||||
- infer missing context
|
||||
- ask the user questions
|
||||
- negotiate scope
|
||||
|
||||
If context is insufficient:
|
||||
Return exactly:
|
||||
@@ -64,53 +52,69 @@ Return exactly:
|
||||
|
||||
---
|
||||
|
||||
## Output Rules (Strict)
|
||||
The Architect output MUST be:
|
||||
## Output Format (MANDATORY AND FINAL)
|
||||
|
||||
The Architect output MUST contain **ONLY these three sections**, in this order:
|
||||
|
||||
### Diagnosis
|
||||
- 3–6 bullet points
|
||||
- Each bullet = one concrete violation
|
||||
- No explanation
|
||||
- No examples
|
||||
- No theory
|
||||
- each bullet = ONE concrete architectural violation or constraint
|
||||
- no explanations
|
||||
- no theory
|
||||
- no examples
|
||||
|
||||
### Plan
|
||||
- 3–10 numbered steps
|
||||
- Each step = one imperative architectural action
|
||||
- No options
|
||||
- No alternatives
|
||||
- No “consider”
|
||||
- No “if”
|
||||
- 3–12 numbered steps
|
||||
- each step = ONE concrete architectural action
|
||||
- imperative form
|
||||
- no alternatives
|
||||
- no “consider”
|
||||
- no “could”
|
||||
- no “should”
|
||||
- no references to other modes
|
||||
|
||||
This IS the plan.
|
||||
Not a description of a plan.
|
||||
|
||||
### Summary
|
||||
- 1–2 short sentences
|
||||
- Direction only
|
||||
|
||||
Nothing else is allowed.
|
||||
- state the architectural direction only
|
||||
- no meta commentary
|
||||
|
||||
---
|
||||
|
||||
## Behavior
|
||||
## Examples of FORBIDDEN Output
|
||||
❌ “I have provided a detailed plan…”
|
||||
❌ “This plan is ready for implementation…”
|
||||
❌ “The following plan outlines…”
|
||||
❌ “Code mode can now implement…”
|
||||
❌ “Next steps would be…”
|
||||
|
||||
These are **never allowed**.
|
||||
|
||||
---
|
||||
|
||||
## Behavior Rules
|
||||
The Architect MUST:
|
||||
- call out violations immediately
|
||||
- stay factual
|
||||
- stay concise
|
||||
- never block execution
|
||||
- never soften rules
|
||||
- state architecture decisions directly
|
||||
- give clear instructions
|
||||
- remain concise
|
||||
- never hedge
|
||||
- never explain why principles exist
|
||||
- never soften instructions
|
||||
|
||||
The Architect MUST NOT:
|
||||
- explain *why* rules exist
|
||||
- teach principles
|
||||
- describe implementation
|
||||
- discuss tests
|
||||
- discuss UX or design
|
||||
- produce long text
|
||||
- output meta summaries
|
||||
- explain process
|
||||
- describe intent
|
||||
- teach Clean Architecture
|
||||
- discuss tooling unless it is the architectural subject itself
|
||||
|
||||
---
|
||||
|
||||
## Completion
|
||||
A response is valid when:
|
||||
- Clean Architecture is enforced
|
||||
- OOP boundaries are clear
|
||||
- SOLID/KISS/YAGNI are respected
|
||||
- Output contains only Diagnosis, Plan, Summary
|
||||
The Architect response is valid ONLY if:
|
||||
- the Diagnosis lists real issues
|
||||
- the Plan contains concrete architectural steps
|
||||
- the Summary states direction
|
||||
- NO meta text exists
|
||||
112
.roo/rules-ask/rules.md
Normal file
112
.roo/rules-ask/rules.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# ❓ Ask
|
||||
|
||||
## Purpose
|
||||
Resolve **meaning, intent, and decision clarity** by consulting existing knowledge,
|
||||
especially **product decisions stored in Memory (MCP)**.
|
||||
|
||||
Ask mode exists to prevent re-deciding things that are already decided.
|
||||
|
||||
---
|
||||
|
||||
## Core Responsibility
|
||||
Ask mode MUST:
|
||||
- interpret the user’s intent at a semantic level
|
||||
- check whether relevant decisions or constraints already exist in Memory
|
||||
- surface those decisions clearly
|
||||
- collapse ambiguity into a single, stable interpretation
|
||||
|
||||
Ask mode does NOT create new decisions.
|
||||
|
||||
---
|
||||
|
||||
## Memory Usage (Primary Function)
|
||||
Ask mode MUST:
|
||||
- consult Memory for existing **product decisions, invariants, or constraints**
|
||||
- identify whether the current question is already answered by Memory
|
||||
- report the relevant decision verbatim (or summarized factually)
|
||||
|
||||
Ask mode MUST NOT:
|
||||
- write to Memory
|
||||
- reinterpret Memory entries
|
||||
- extend Memory
|
||||
- challenge stored decisions
|
||||
|
||||
Memory is treated as **truth**.
|
||||
|
||||
---
|
||||
|
||||
## What Ask Produces
|
||||
Ask mode produces:
|
||||
- clarification of intent
|
||||
- identification of applicable existing decisions
|
||||
- resolution of ambiguity using known constraints
|
||||
|
||||
Ask mode does NOT:
|
||||
- plan work
|
||||
- assign tasks
|
||||
- suggest changes
|
||||
- evaluate architecture
|
||||
- propose solutions
|
||||
- gather technical context
|
||||
- scan the repository
|
||||
|
||||
---
|
||||
|
||||
## Output Rules (STRICT)
|
||||
Ask mode output MUST:
|
||||
- be **1–3 short lines**
|
||||
- contain **statements only**
|
||||
- contain **no questions**
|
||||
- contain **no options or alternatives**
|
||||
- contain **no explanation or justification**
|
||||
- contain **no technical detail**
|
||||
|
||||
Typical output forms:
|
||||
- “This is already decided: X.”
|
||||
- “Memory defines Y as invariant.”
|
||||
- “Intent resolves to Z under existing constraints.”
|
||||
- “No prior decision exists for this.”
|
||||
|
||||
---
|
||||
|
||||
## If Memory Has No Relevant Entry
|
||||
If Memory contains no relevant decision:
|
||||
- Ask mode states this explicitly
|
||||
- Ask mode does NOT invent a resolution
|
||||
|
||||
Exact output:
|
||||
“No existing decision found.”
|
||||
|
||||
---
|
||||
|
||||
## Context Handling
|
||||
Ask mode operates ONLY on:
|
||||
- the user’s stated intent
|
||||
- context provided by the Orchestrator
|
||||
- Memory contents
|
||||
|
||||
Ask mode MUST NOT:
|
||||
- ask the user questions
|
||||
- infer missing information
|
||||
- guess intent
|
||||
- expand scope
|
||||
|
||||
---
|
||||
|
||||
## Forbidden
|
||||
Ask mode MUST NOT:
|
||||
- act as Clarification-by-interrogation
|
||||
- perform analysis beyond semantics
|
||||
- explain concepts
|
||||
- teach principles
|
||||
- propose actions
|
||||
- contradict Memory
|
||||
- block execution
|
||||
|
||||
---
|
||||
|
||||
## Completion
|
||||
Ask mode is complete when:
|
||||
- applicable Memory decisions are identified (or absence is confirmed)
|
||||
- intent is unambiguous
|
||||
- no further semantic clarification is needed
|
||||
@@ -13,9 +13,9 @@ Each mode has **one responsibility** and performs **only that responsibility**:
|
||||
|
||||
- Orchestrator
|
||||
- Architect
|
||||
- Clarification
|
||||
- Ask
|
||||
- Debugger
|
||||
- Backend Coder
|
||||
- Coder
|
||||
- Frontend Coder
|
||||
- Designer
|
||||
- Quality
|
||||
@@ -115,6 +115,58 @@ execution must proceed without further comment.
|
||||
|
||||
---
|
||||
|
||||
## Memory (MCP) — Product Brain Rules
|
||||
|
||||
Memory is **not** a place for instructions, prompts, or process rules.
|
||||
|
||||
Memory represents **product knowledge and decision constraints** that are:
|
||||
- not encoded directly in code
|
||||
- not part of working instructions
|
||||
- but must influence future decisions
|
||||
|
||||
### What MAY be stored in Memory
|
||||
Only **truths about the product**, such as:
|
||||
- domain rules that are not obvious from code alone
|
||||
- irreversible product decisions
|
||||
- historical or business constraints
|
||||
- intentional trade-offs
|
||||
- invariants that must hold across all future work
|
||||
|
||||
Examples (illustrative only):
|
||||
- “The website is marketing-only and contains no business rules.”
|
||||
- “Public DTOs are external contracts and must not be renamed.”
|
||||
- “Some automation flows intentionally allow partial failure.”
|
||||
|
||||
Memory entries must be:
|
||||
- declarative
|
||||
- short
|
||||
- atomic
|
||||
- free of explanation
|
||||
- free of history
|
||||
- free of instruction language
|
||||
|
||||
### What MUST NEVER be stored in Memory
|
||||
- instructions or rules of how to work
|
||||
- role definitions or mode behavior
|
||||
- prompts or prompt fragments
|
||||
- TODOs or task lists
|
||||
- explanations or examples
|
||||
- chat summaries
|
||||
- code
|
||||
- logs
|
||||
- decisions about process
|
||||
|
||||
If something belongs in a prompt, README, or code comment → it does NOT belong in memory.
|
||||
|
||||
### Memory Access Rules
|
||||
- Only the **Orchestrator** may read from or write to memory.
|
||||
- Experts must NEVER read or write memory directly.
|
||||
- Memory is consulted only when making decisions, never during execution.
|
||||
|
||||
Memory exists to **prevent re-deciding facts**, not to guide implementation.
|
||||
|
||||
---
|
||||
|
||||
## Forbidden (Applies to All Modes)
|
||||
Modes may NOT:
|
||||
- override user intent
|
||||
|
||||
184
README.docker.md
Normal file
184
README.docker.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Docker Setup for GridPilot
|
||||
|
||||
This document describes the Docker setup for local development and production deployment of GridPilot.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Development
|
||||
|
||||
Start all services with hot-reloading:
|
||||
```bash
|
||||
npm run docker:dev:build
|
||||
```
|
||||
|
||||
This will:
|
||||
- Start PostgreSQL database on port 5432
|
||||
- Start API on port 3000 (with debugger on 9229)
|
||||
- Start Website on port 3001
|
||||
- Enable hot-reloading for both apps
|
||||
|
||||
Access:
|
||||
- Website: http://localhost:3001
|
||||
- API: http://localhost:3000
|
||||
- Database: localhost:5432
|
||||
|
||||
### Production
|
||||
|
||||
Start all services in production mode:
|
||||
```bash
|
||||
npm run docker:prod:build
|
||||
```
|
||||
|
||||
This will:
|
||||
- Build optimized Docker images
|
||||
- Start PostgreSQL, Redis, API, Website, and Nginx
|
||||
- Enable health checks, auto-restart, and resource limits
|
||||
- Configure caching and performance optimizations
|
||||
|
||||
Access:
|
||||
- Nginx (Website + API): http://localhost:80
|
||||
|
||||
## Available Commands
|
||||
|
||||
### Development
|
||||
- `npm run docker:dev` - Start dev environment
|
||||
- `npm run docker:dev:build` - Rebuild and start
|
||||
- `npm run docker:dev:down` - Stop services
|
||||
- `npm run docker:dev:logs` - View logs
|
||||
- `npm run docker:dev:clean` - Stop and remove volumes
|
||||
|
||||
### Production
|
||||
- `npm run docker:prod` - Start prod environment
|
||||
- `npm run docker:prod:build` - Rebuild and start
|
||||
- `npm run docker:prod:down` - Stop services
|
||||
- `npm run docker:prod:logs` - View logs
|
||||
- `npm run docker:prod:clean` - Stop and remove volumes
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Development (.env.development)
|
||||
Copy and customize as needed. Default values work out of the box.
|
||||
|
||||
### Production (.env.production)
|
||||
**IMPORTANT**: Update these before deploying:
|
||||
- Database credentials (POSTGRES_PASSWORD, DATABASE_URL)
|
||||
- API URLs (NEXT_PUBLIC_API_URL, NEXT_PUBLIC_SITE_URL)
|
||||
- Vercel KV credentials (required for production)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Development Setup
|
||||
- Hot-reloading enabled via volume mounts
|
||||
- Source code changes reflect immediately
|
||||
- Database persisted in named volume
|
||||
- Debug port exposed for API (9229)
|
||||
|
||||
### Production Setup
|
||||
- Multi-stage builds for optimized images
|
||||
- Only production dependencies included
|
||||
- Nginx reverse proxy for both services
|
||||
- Health checks for all services
|
||||
- Auto-restart on failure
|
||||
|
||||
## Docker Services
|
||||
|
||||
### API (NestJS)
|
||||
- Dev: `apps/api/Dockerfile.dev`
|
||||
- Prod: `apps/api/Dockerfile.prod`
|
||||
- Port: 3000
|
||||
- Debug: 9229 (dev only)
|
||||
|
||||
### Website (Next.js)
|
||||
- Dev: `apps/website/Dockerfile.dev`
|
||||
- Prod: `apps/website/Dockerfile.prod`
|
||||
- Port: 3001 (dev), 3000 (prod)
|
||||
|
||||
### Database (PostgreSQL)
|
||||
- Image: postgres:15-alpine
|
||||
- Port: 5432 (internal)
|
||||
- Data: Persisted in Docker volume
|
||||
- Optimized with performance tuning parameters
|
||||
|
||||
### Redis (Production only)
|
||||
- Image: redis:7-alpine
|
||||
- Port: 6379 (internal)
|
||||
- Configured with:
|
||||
- LRU eviction policy
|
||||
- 512MB max memory
|
||||
- AOF persistence
|
||||
- Password protection
|
||||
|
||||
### Nginx (Production only)
|
||||
- Reverse proxy for website + API
|
||||
- Features:
|
||||
- Rate limiting (API: 10r/s, General: 30r/s)
|
||||
- Security headers (XSS, CSP, Frame-Options)
|
||||
- Gzip compression
|
||||
- Static asset caching
|
||||
- Connection pooling
|
||||
- Request buffering
|
||||
- Port: 80, 443
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Services won't start
|
||||
```bash
|
||||
# Clean everything and rebuild
|
||||
npm run docker:dev:clean
|
||||
npm run docker:dev:build
|
||||
```
|
||||
|
||||
### Hot-reloading not working
|
||||
Check that volume mounts are correct in docker-compose.dev.yml
|
||||
|
||||
### Database connection issues
|
||||
Ensure DATABASE_URL in .env matches the database service configuration
|
||||
|
||||
### Check logs
|
||||
```bash
|
||||
# All services
|
||||
npm run docker:dev:logs
|
||||
|
||||
# Specific service
|
||||
docker-compose -f docker-compose.dev.yml logs -f api
|
||||
docker-compose -f docker-compose.dev.yml logs -f website
|
||||
docker-compose -f docker-compose.dev.yml logs -f db
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
1. **First time setup**: Use `docker:dev:build` to ensure images are built
|
||||
2. **Clean slate**: Use `docker:dev:clean` to remove all data and start fresh
|
||||
3. **Production testing**: Test prod setup locally before deploying
|
||||
4. **Database access**: Use any PostgreSQL client with credentials from .env file
|
||||
5. **Debugging**: Attach debugger to port 9229 for API debugging
|
||||
|
||||
## Production Deployment
|
||||
|
||||
Before deploying to production:
|
||||
|
||||
1. Update `.env.production` with real credentials
|
||||
2. Configure SSL certificates in `nginx/ssl/`
|
||||
3. Update Nginx configuration for HTTPS
|
||||
4. Set proper domain names in environment variables
|
||||
5. Consider using Docker secrets for sensitive data
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── docker-compose.dev.yml # Development orchestration
|
||||
├── docker-compose.prod.yml # Production orchestration
|
||||
├── .env.development # Dev environment variables
|
||||
├── .env.production # Prod environment variables
|
||||
├── apps/
|
||||
│ ├── api/
|
||||
│ │ ├── Dockerfile.dev # API dev image
|
||||
│ │ ├── Dockerfile.prod # API prod image
|
||||
│ │ └── .dockerignore
|
||||
│ └── website/
|
||||
│ ├── Dockerfile.dev # Website dev image
|
||||
│ ├── Dockerfile.prod # Website prod image
|
||||
│ └── .dockerignore
|
||||
└── nginx/
|
||||
└── nginx.conf # Nginx configuration
|
||||
20
apps/website/.dockerignore
Normal file
20
apps/website/.dockerignore
Normal file
@@ -0,0 +1,20 @@
|
||||
node_modules
|
||||
.next
|
||||
dist
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
Dockerfile*
|
||||
docker-compose.*
|
||||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
.DS_Store
|
||||
*.md
|
||||
.vscode
|
||||
.idea
|
||||
coverage
|
||||
.turbo
|
||||
23
apps/website/Dockerfile.dev
Normal file
23
apps/website/Dockerfile.dev
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install bash for better shell capabilities
|
||||
RUN apk add --no-cache bash
|
||||
|
||||
# Copy root package.json and install dependencies
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
RUN find ./node_modules -name "next" -print || true # Debugging line
|
||||
|
||||
# Copy apps/website and packages for development
|
||||
COPY apps/website apps/website/
|
||||
COPY packages packages/
|
||||
COPY apps/website/tsconfig.json apps/website/
|
||||
COPY scripts scripts/
|
||||
COPY tsconfig.base.json ./
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Run from the correct workspace context
|
||||
CMD ["npm", "run", "dev", "--workspace=@gridpilot/website"]
|
||||
48
apps/website/Dockerfile.prod
Normal file
48
apps/website/Dockerfile.prod
Normal file
@@ -0,0 +1,48 @@
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files and install dependencies
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy apps/website, packages, and config for building
|
||||
COPY apps/website apps/website/
|
||||
COPY packages packages/
|
||||
COPY apps/website/tsconfig.json apps/website/
|
||||
COPY scripts scripts/
|
||||
COPY tsconfig.base.json ./
|
||||
|
||||
|
||||
# Build the Next.js application
|
||||
# Run from the root workspace context
|
||||
RUN node ./node_modules/next/dist/bin/next build
|
||||
|
||||
|
||||
# Production stage: slim image with only production dependencies and built files
|
||||
FROM node:20-alpine AS production_final
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install wget for healthchecks
|
||||
RUN apk add --no-cache wget
|
||||
|
||||
|
||||
# Copy package files and install production dependencies only
|
||||
COPY --from=builder /app/package.json ./
|
||||
COPY --from=builder /app/package-lock.json ./
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
# Copy built Next.js application
|
||||
COPY --from=builder /app/apps/website/.next ./apps/website/.next
|
||||
COPY --from=builder /app/apps/website/public ./apps/website/public
|
||||
COPY --from=builder /app/apps/website/package.json ./apps/website/package.json
|
||||
COPY --from=builder /app/apps/website/next.config.mjs ./apps/website/next.config.mjs
|
||||
|
||||
# Copy packages (needed for runtime dependencies)
|
||||
COPY --from=builder /app/packages ./packages
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
# Run Next.js in production mode from the root workspace context
|
||||
CMD ["node", "./node_modules/next/dist/bin/next", "start"]
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
// Fix for Next.js 13+ Turbopack in monorepos to correctly identify the workspace root
|
||||
outputFileTracingRoot: '../../',
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
@@ -17,9 +19,6 @@ const nextConfig = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: false,
|
||||
},
|
||||
eslint: {
|
||||
ignoreDuringBuilds: false,
|
||||
},
|
||||
transpilePackages: [
|
||||
'@gridpilot/racing',
|
||||
'@gridpilot/identity',
|
||||
|
||||
60
docker-compose.dev.yml
Normal file
60
docker-compose.dev.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/api/Dockerfile.dev
|
||||
env_file:
|
||||
- .env.development
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "9229:9229"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- gridpilot-network
|
||||
restart: unless-stopped
|
||||
|
||||
website:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/website/Dockerfile.dev
|
||||
env_file:
|
||||
- .env.development
|
||||
environment:
|
||||
- NEXT_TELEMETRY_DISABLED=1
|
||||
ports:
|
||||
- "3001:3000"
|
||||
networks:
|
||||
- gridpilot-network
|
||||
restart: unless-stopped
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=${POSTGRES_DB:-gridpilot_db}
|
||||
- POSTGRES_USER=${POSTGRES_USER:-user}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- dev_db_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- gridpilot-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-user} -d ${POSTGRES_DB:-gridpilot_db}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
networks:
|
||||
gridpilot-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
dev_db_data:
|
||||
205
docker-compose.prod.yml
Normal file
205
docker-compose.prod.yml
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/api/Dockerfile.prod
|
||||
env_file:
|
||||
- .env.production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
expose:
|
||||
- "3000"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- gridpilot-network
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
website:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/website/Dockerfile.prod
|
||||
env_file:
|
||||
- .env.production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- NEXT_TELEMETRY_DISABLED=1
|
||||
expose:
|
||||
- "3000"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- gridpilot-network
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=${POSTGRES_DB:-gridpilot_db}
|
||||
- POSTGRES_USER=${POSTGRES_USER:-user}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
||||
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
||||
command: >
|
||||
postgres
|
||||
-c shared_buffers=256MB
|
||||
-c max_connections=200
|
||||
-c effective_cache_size=1GB
|
||||
-c maintenance_work_mem=64MB
|
||||
-c checkpoint_completion_target=0.9
|
||||
-c wal_buffers=16MB
|
||||
-c default_statistics_target=100
|
||||
-c random_page_cost=1.1
|
||||
-c effective_io_concurrency=200
|
||||
-c work_mem=2621kB
|
||||
-c min_wal_size=1GB
|
||||
-c max_wal_size=4GB
|
||||
expose:
|
||||
- "5432"
|
||||
volumes:
|
||||
- prod_db_data:/var/lib/postgresql/data
|
||||
- ./backups:/backups
|
||||
networks:
|
||||
- gridpilot-network
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-user} -d ${POSTGRES_DB:-gridpilot_db}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: >
|
||||
redis-server
|
||||
--maxmemory 512mb
|
||||
--maxmemory-policy allkeys-lru
|
||||
--save 60 1000
|
||||
--appendonly yes
|
||||
--appendfsync everysec
|
||||
--requirepass ${REDIS_PASSWORD:-CHANGE_ME_IN_PRODUCTION}
|
||||
expose:
|
||||
- "6379"
|
||||
volumes:
|
||||
- prod_redis_data:/data
|
||||
networks:
|
||||
- gridpilot-network
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 768M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 256M
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "5m"
|
||||
max-file: "2"
|
||||
|
||||
nginx:
|
||||
image: nginx:stable-alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- nginx_cache:/var/cache/nginx
|
||||
depends_on:
|
||||
- api
|
||||
- website
|
||||
networks:
|
||||
- gridpilot-network
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
gridpilot-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
prod_db_data:
|
||||
prod_redis_data:
|
||||
nginx_cache:
|
||||
215
nginx/nginx.conf
Normal file
215
nginx/nginx.conf
Normal file
@@ -0,0 +1,215 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 2048;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||
'rt=$request_time uct="$upstream_connect_time" '
|
||||
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# Performance optimizations
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
keepalive_requests 100;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# Buffer optimizations
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 20M;
|
||||
client_header_buffer_size 1k;
|
||||
large_client_header_buffers 4 8k;
|
||||
output_buffers 1 32k;
|
||||
postpone_output 1460;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript
|
||||
application/json application/javascript application/xml+rss
|
||||
application/rss+xml font/truetype font/opentype
|
||||
application/vnd.ms-fontobject image/svg+xml;
|
||||
gzip_disable "msie6";
|
||||
|
||||
# Brotli compression (if available)
|
||||
# brotli on;
|
||||
# brotli_comp_level 6;
|
||||
# brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
# Cache settings
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache:10m max_size=1g inactive=60m use_temp_path=off;
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
proxy_cache_valid 200 60m;
|
||||
proxy_cache_valid 404 1m;
|
||||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||||
proxy_cache_background_update on;
|
||||
proxy_cache_lock on;
|
||||
|
||||
# Upstreams
|
||||
upstream api {
|
||||
server api:3000 max_fails=3 fail_timeout=30s;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream website {
|
||||
server website:3000 max_fails=3 fail_timeout=30s;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Rate limiting zones
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=30r/s;
|
||||
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
|
||||
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||
|
||||
# HTTP server - redirect to HTTPS in production
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# Health check endpoint (no redirect)
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# For development/testing, serve directly
|
||||
# In production, uncomment the redirect below
|
||||
# return 301 https://$host$request_uri;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||
|
||||
# Connection limits
|
||||
limit_conn conn_limit 10;
|
||||
|
||||
# API routes
|
||||
location /api/ {
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
limit_req_status 429;
|
||||
|
||||
proxy_pass http://api/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Headers
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# Buffering
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# API health check (no rate limit)
|
||||
location /api/health {
|
||||
access_log off;
|
||||
proxy_pass http://api/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# Static assets with caching
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
proxy_pass http://website;
|
||||
proxy_cache cache;
|
||||
proxy_cache_valid 200 7d;
|
||||
proxy_cache_valid 404 1m;
|
||||
add_header Cache-Control "public, max-age=604800, immutable";
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
}
|
||||
|
||||
# Website routes
|
||||
location / {
|
||||
limit_req zone=general_limit burst=50 nodelay;
|
||||
limit_req_status 429;
|
||||
|
||||
proxy_pass http://website/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Headers
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# Buffering
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS server (configure when SSL is ready)
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name your-domain.com;
|
||||
#
|
||||
# # SSL configuration
|
||||
# ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
# ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
# ssl_prefer_server_ciphers off;
|
||||
# ssl_session_cache shared:SSL:10m;
|
||||
# ssl_session_timeout 10m;
|
||||
# ssl_session_tickets off;
|
||||
# ssl_stapling on;
|
||||
# ssl_stapling_verify on;
|
||||
#
|
||||
# # HSTS (uncomment after testing)
|
||||
# # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||
#
|
||||
# # ... rest of location blocks from HTTP server
|
||||
# }
|
||||
}
|
||||
6598
package-lock.json
generated
6598
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -13,7 +13,19 @@
|
||||
"scripts": {
|
||||
"dev": "echo 'Development server placeholder - to be configured'",
|
||||
"build": "echo 'Build all packages placeholder - to be configured'",
|
||||
"docker:dev": "docker-compose -f docker-compose.dev.yml up",
|
||||
"docker:dev:build": "docker-compose -f docker-compose.dev.yml up --build",
|
||||
"docker:dev:down": "docker-compose -f docker-compose.dev.yml down",
|
||||
"docker:dev:logs": "docker-compose -f docker-compose.dev.yml logs -f",
|
||||
"docker:dev:clean": "docker-compose -f docker-compose.dev.yml down -v",
|
||||
"docker:prod": "docker-compose -f docker-compose.prod.yml up -d",
|
||||
"docker:prod:build": "docker-compose -f docker-compose.prod.yml up -d --build",
|
||||
"docker:prod:down": "docker-compose -f docker-compose.prod.yml down",
|
||||
"docker:prod:logs": "docker-compose -f docker-compose.prod.yml logs -f",
|
||||
"docker:prod:clean": "docker-compose -f docker-compose.prod.yml down -v",
|
||||
"api:build": "npm run build --workspace=@gridpilot/api",
|
||||
"test": "vitest run && vitest run --config vitest.e2e.config.ts && npm run smoke:website",
|
||||
"test:api": "npx jest --config=apps/api/jest.config.js",
|
||||
"test:unit": "vitest run tests/unit",
|
||||
"test:integration": "vitest run tests/integration",
|
||||
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
||||
@@ -56,6 +68,7 @@
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/jsdom": "^27.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/express": "^4.17.21",
|
||||
"@vitest/ui": "^2.1.8",
|
||||
"cheerio": "^1.0.0",
|
||||
"commander": "^11.0.0",
|
||||
|
||||
@@ -5,9 +5,19 @@
|
||||
* Kept in domain/types so domain/entities contains only entity classes.
|
||||
*/
|
||||
|
||||
export type EntityType = 'league' | 'driver' | 'team' | 'race' | 'sponsor';
|
||||
export enum EntityType {
|
||||
LEAGUE = 'league',
|
||||
DRIVER = 'driver',
|
||||
TEAM = 'team',
|
||||
RACE = 'race',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
|
||||
export type VisitorType = 'anonymous' | 'driver' | 'sponsor';
|
||||
export enum VisitorType {
|
||||
ANONYMOUS = 'anonymous',
|
||||
DRIVER = 'driver',
|
||||
SPONSOR = 'sponsor',
|
||||
}
|
||||
|
||||
export interface PageViewProps {
|
||||
id: string;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB |
@@ -19,6 +19,8 @@
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"paths": {
|
||||
"@/*": ["./*"],
|
||||
"@/lib/*": ["apps/website/lib/*"],
|
||||
|
||||
Reference in New Issue
Block a user