Multi-stage Dockerfile builds the Vite app in Node 22 and serves static assets from nginx:alpine. Includes gzip compression, SPA fallback routing, immutable cache headers for hashed assets, and configurable port mapping (default 8080). Deploy with `docker compose up -d`.
83 lines
3.7 KiB
Markdown
83 lines
3.7 KiB
Markdown
## ADDED Requirements
|
|
|
|
### Requirement: Multi-stage Docker build
|
|
The Dockerfile SHALL use a multi-stage build where stage 1 installs dependencies and builds the Vite app, and stage 2 copies only the built static assets into an nginx:alpine image.
|
|
|
|
#### Scenario: Build produces a working image
|
|
- **WHEN** `docker compose build` is run from the project root
|
|
- **THEN** a Docker image is produced that contains only nginx and the built static files from `app/dist/`
|
|
|
|
#### Scenario: Node.js is not present in the final image
|
|
- **WHEN** the final Docker image is inspected
|
|
- **THEN** it MUST NOT contain Node.js, npm, or `node_modules`
|
|
|
|
### Requirement: Single-command deployment with docker compose
|
|
The project SHALL include a `docker-compose.yml` that builds and runs the app with `docker compose up`.
|
|
|
|
#### Scenario: Start the app
|
|
- **WHEN** `docker compose up -d` is run from the project root
|
|
- **THEN** the app is built (if needed) and served on the configured host port
|
|
|
|
#### Scenario: Rebuild after code changes
|
|
- **WHEN** `docker compose up -d --build` is run after source code changes
|
|
- **THEN** the image is rebuilt with the latest code and the container is replaced
|
|
|
|
### Requirement: nginx serves the SPA correctly
|
|
nginx SHALL serve the app's static files and fall back to `index.html` for all non-file routes.
|
|
|
|
#### Scenario: Root path serves the app
|
|
- **WHEN** a browser requests `/`
|
|
- **THEN** nginx responds with `index.html` and HTTP 200
|
|
|
|
#### Scenario: Hashed asset files are served
|
|
- **WHEN** a browser requests a path under `/assets/` (e.g., `/assets/index-abc123.js`)
|
|
- **THEN** nginx responds with the file and HTTP 200
|
|
|
|
#### Scenario: Unknown paths fall back to index.html
|
|
- **WHEN** a browser requests a path that does not match any file (e.g., `/some/route`)
|
|
- **THEN** nginx responds with `index.html` and HTTP 200
|
|
|
|
### Requirement: Gzip compression enabled
|
|
nginx SHALL serve gzip-compressed responses for text-based content types.
|
|
|
|
#### Scenario: JavaScript files are compressed
|
|
- **WHEN** a browser requests a `.js` file with `Accept-Encoding: gzip`
|
|
- **THEN** the response MUST include `Content-Encoding: gzip`
|
|
|
|
#### Scenario: CSS files are compressed
|
|
- **WHEN** a browser requests a `.css` file with `Accept-Encoding: gzip`
|
|
- **THEN** the response MUST include `Content-Encoding: gzip`
|
|
|
|
### Requirement: Cache headers for hashed and unhashed assets
|
|
nginx SHALL set long-lived cache headers for content-hashed assets and no-cache for `index.html`.
|
|
|
|
#### Scenario: Hashed assets get immutable caching
|
|
- **WHEN** a browser requests a file under `/assets/`
|
|
- **THEN** the response MUST include `Cache-Control: public, max-age=31536000, immutable`
|
|
|
|
#### Scenario: index.html is not cached
|
|
- **WHEN** a browser requests `/` or `/index.html`
|
|
- **THEN** the response MUST include `Cache-Control: no-cache`
|
|
|
|
### Requirement: .dockerignore excludes unnecessary files
|
|
A `.dockerignore` file SHALL prevent large or irrelevant directories from being included in the Docker build context.
|
|
|
|
#### Scenario: node_modules excluded
|
|
- **WHEN** Docker reads the build context
|
|
- **THEN** `node_modules/` directories MUST be excluded
|
|
|
|
#### Scenario: .git excluded
|
|
- **WHEN** Docker reads the build context
|
|
- **THEN** the `.git/` directory MUST be excluded
|
|
|
|
### Requirement: Port mapping via docker-compose
|
|
The `docker-compose.yml` SHALL map a host port to the container's port 80, defaulting to host port 8080.
|
|
|
|
#### Scenario: Default port mapping
|
|
- **WHEN** `docker compose up` is run without overrides
|
|
- **THEN** the app MUST be accessible at `http://localhost:8080`
|
|
|
|
#### Scenario: Custom port via environment variable
|
|
- **WHEN** the user sets an environment variable or edits the compose file to change the host port
|
|
- **THEN** the app MUST be accessible on the configured port
|