bind: MLflow port to 127.0.0.1:5000:5000 in docker-compose.yml

Changes:
- Updated docker-compose.yml MLflow service port binding from 5000:5000 to 127.0.0.1:5000:5000
  to restrict access to localhost only for security
- Marked task 1.7 as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-18 10:58:11 +01:00
parent 9efa1dbbcc
commit c327ba3370
19 changed files with 1002 additions and 2 deletions

View file

@ -0,0 +1,74 @@
## ADDED Requirements
### Requirement: Generic error responses
All Next.js API routes SHALL return generic error messages to clients for 500-level errors. The response body SHALL be `{ "error": "Internal server error" }`. The full error details SHALL be logged server-side via `console.error` with request context.
#### Scenario: Internal error returns generic message
- **WHEN** an API route handler throws an unexpected error
- **THEN** the client receives HTTP 500 with `{ "error": "Internal server error" }` and the full error is logged server-side
#### Scenario: No stack traces in response
- **WHEN** a database query fails in any API route
- **THEN** the response does NOT contain table names, file paths, connection strings, or stack traces
### Requirement: Scoped bulk annotation delete
The `DELETE /api/annotations` endpoint SHALL require a `chartId` query parameter when `all=true` is specified. Unscoped delete-all (without chartId) SHALL be rejected with HTTP 400.
#### Scenario: Scoped bulk delete
- **WHEN** `DELETE /api/annotations?all=true&chartId=5` is called
- **THEN** all annotations for chart 5 are deleted
#### Scenario: Unscoped bulk delete rejected
- **WHEN** `DELETE /api/annotations?all=true` is called without chartId
- **THEN** the route returns HTTP 400 with `{ "error": "chartId is required for bulk delete" }`
### Requirement: Transaction-wrapped chart cascade delete
The `DELETE /api/charts/[id]` route SHALL wrap all related deletions (annotations, span annotations, candles, chart) in a single database transaction using `db.transaction()`.
#### Scenario: Cascade delete in transaction
- **WHEN** `DELETE /api/charts/5` is called
- **THEN** span annotations, annotations, candles, and the chart record for chart 5 are all deleted within a single transaction
#### Scenario: Partial failure rolls back
- **WHEN** the candles deletion fails mid-transaction
- **THEN** all deletions are rolled back and the chart remains intact
### Requirement: Span annotations included in chart cascade delete
The `DELETE /api/charts/[id]` route SHALL delete span annotations for the chart in addition to annotations and candles.
#### Scenario: Span annotations deleted with chart
- **WHEN** `DELETE /api/charts/5` is called
- **THEN** all `span_annotations` rows with `chart_id=5` are deleted before the chart record
### Requirement: parseInt validation
All API routes that parse integer query parameters SHALL use `parseInt(value, 10)` with radix 10 and check for `isNaN()`. Invalid integer parameters SHALL return HTTP 400.
#### Scenario: Valid integer parameter
- **WHEN** `GET /api/candles?chartId=5` is called
- **THEN** chartId is parsed as integer 5
#### Scenario: Invalid integer parameter
- **WHEN** `GET /api/candles?chartId=abc` is called
- **THEN** the route returns HTTP 400 with `{ "error": "Invalid chartId" }`
### Requirement: CSV injection protection on exports
All CSV export routes SHALL prefix cell values starting with `=`, `+`, `-`, or `@` with a single quote (`'`) to prevent spreadsheet formula injection.
#### Scenario: Dangerous cell value escaped
- **WHEN** an annotation note contains `=CMD("calc")`
- **THEN** the exported CSV cell contains `'=CMD("calc")`
#### Scenario: Normal values unchanged
- **WHEN** an annotation note contains `regular text`
- **THEN** the exported CSV cell contains `regular text` (no prefix)
### Requirement: response.ok checks on all fetch calls
All `fetch()` calls in frontend components (`page.tsx`, `CandleChart.tsx`) SHALL check `response.ok` before calling `response.json()`. If `!response.ok`, the code SHALL throw an error or handle the failure explicitly.
#### Scenario: Successful response parsed
- **WHEN** a fetch call returns HTTP 200
- **THEN** `response.json()` is called and the data is used normally
#### Scenario: Error response handled
- **WHEN** a fetch call returns HTTP 500
- **THEN** the code detects `!response.ok` and shows an error message instead of attempting JSON parse