Rename API contracts to web routes for clarity
Changed terminology from "API" to "Routes" to better reflect server-rendered HTML approach: - Renamed submission_api.md → submission_routes.md - Renamed dashboard_api.md → dashboard_routes.md - Renamed admin_api.md → admin_routes.md - Updated headers to clarify "Response Type: Server-rendered HTML (no JavaScript required)" - Updated references in plan.md and quickstart.md This clarifies that the application uses traditional web routes with form submissions and HTML responses, not REST API endpoints with JSON. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
# Submission Routes Contract
|
||||
|
||||
**Scope**: Anonymous feedback submission web routes (User Story P1)
|
||||
**Authentication**: None (anonymous access)
|
||||
**Response Type**: Server-rendered HTML (no JavaScript required)
|
||||
|
||||
---
|
||||
|
||||
## GET /submit/{product_slug}
|
||||
|
||||
Display the feedback submission form for a specific product.
|
||||
|
||||
### Request
|
||||
|
||||
**Path Parameters**:
|
||||
- `product_slug` (string, required): Product's URL-safe identifier
|
||||
|
||||
**Headers**: None required
|
||||
|
||||
**Query Parameters**: None
|
||||
|
||||
### Response
|
||||
|
||||
**Success (200 OK)**:
|
||||
```html
|
||||
Content-Type: text/html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Submit Feedback - {Product Name}</title></head>
|
||||
<body>
|
||||
<h1>Submit Feedback for {Product Name}</h1>
|
||||
<form method="POST" action="/submit/{product_slug}" enctype="multipart/form-data">
|
||||
<textarea name="feedback_text" maxlength="10000"></textarea>
|
||||
<input type="file" name="attachments" multiple accept=".pdf,.docx,.txt,.jpg,.png,.gif,.webp">
|
||||
<button type="submit">Submit Feedback</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Error (404 Not Found)**: Product does not exist or is archived
|
||||
```html
|
||||
Content-Type: text/html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Product Not Found</h1>
|
||||
<p>The product you're looking for does not exist or is no longer accepting feedback.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Functional Requirements Covered
|
||||
- FR-001: Public, unauthenticated submission form
|
||||
- FR-002: Text input up to 10,000 characters
|
||||
- FR-004: Up to 3 file attachments
|
||||
|
||||
---
|
||||
|
||||
## POST /submit/{product_slug}
|
||||
|
||||
Submit anonymous feedback for a specific product.
|
||||
|
||||
### Request
|
||||
|
||||
**Path Parameters**:
|
||||
- `product_slug` (string, required): Product's URL-safe identifier
|
||||
|
||||
**Headers**:
|
||||
- `Content-Type: multipart/form-data`
|
||||
|
||||
**Form Data**:
|
||||
- `feedback_text` (string, optional): Feedback text (0-10,000 characters)
|
||||
- `attachments` (file[], optional): Up to 3 files, max 10MB each
|
||||
|
||||
**Example**:
|
||||
```http
|
||||
POST /submit/acme-app HTTP/1.1
|
||||
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
|
||||
|
||||
------WebKitFormBoundary
|
||||
Content-Disposition: form-data; name="feedback_text"
|
||||
|
||||
The app crashes when uploading files over 5MB.
|
||||
------WebKitFormBoundary
|
||||
Content-Disposition: form-data; name="attachments"; filename="screenshot.png"
|
||||
Content-Type: image/png
|
||||
|
||||
[binary data]
|
||||
------WebKitFormBoundary--
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
**Success (200 OK)**:
|
||||
```html
|
||||
Content-Type: text/html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Thank You!</h1>
|
||||
<p>Your feedback has been submitted successfully.</p>
|
||||
<p>Your feedback ID: {feedback_id}</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Error (400 Bad Request)**: Validation failure
|
||||
```html
|
||||
Content-Type: text/html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Submission Error</h1>
|
||||
<ul>
|
||||
<li>Feedback must contain text or at least one attachment</li>
|
||||
<li>Maximum 3 attachments allowed</li>
|
||||
<li>Text cannot exceed 10,000 characters</li>
|
||||
<li>File size cannot exceed 10MB per file</li>
|
||||
<li>Unsupported file type: {filename}</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Error (413 Payload Too Large)**: File size exceeds limit
|
||||
```html
|
||||
Content-Type: text/html
|
||||
HTTP/1.1 413 Payload Too Large
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>File Too Large</h1>
|
||||
<p>One or more files exceed the 10MB limit.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Error (429 Too Many Requests)**: Rate limit exceeded
|
||||
```html
|
||||
Content-Type: text/html
|
||||
X-RateLimit-Limit: 10
|
||||
X-RateLimit-Remaining: 0
|
||||
X-RateLimit-Reset: 1697456789
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Too Many Submissions</h1>
|
||||
<p>You have exceeded the submission limit of 10 per hour. Please try again later.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Error (451 Unavailable For Legal Reasons)**: Malware detected
|
||||
```html
|
||||
Content-Type: text/html
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Security Error</h1>
|
||||
<p>One or more files failed security scanning. Please ensure your files are safe and try again.</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Validation Rules
|
||||
|
||||
1. **Text Validation** (FR-002, FR-003):
|
||||
- Length: 0-10,000 characters
|
||||
- Encoding: UTF-8, any language accepted
|
||||
- Empty allowed if attachments present
|
||||
|
||||
2. **Attachment Validation** (FR-004, FR-005, FR-006):
|
||||
- Count: 0-3 files
|
||||
- Size: Max 10MB per file
|
||||
- Types: PDF, DOCX, TXT, JPG, PNG, GIF, WebP
|
||||
- MIME type validation (server-side)
|
||||
|
||||
3. **Submission Validation** (FR-011, FR-012):
|
||||
- Must have text OR attachments (not both empty)
|
||||
|
||||
4. **Security** (FR-059, FR-060):
|
||||
- ClamAV virus scan before storage
|
||||
- Filename sanitization (remove path traversal)
|
||||
- MIME type validation
|
||||
|
||||
5. **Rate Limiting** (FR-061):
|
||||
- 10 submissions per hour per IP address
|
||||
|
||||
### Functional Requirements Covered
|
||||
- FR-001 to FR-012: Complete submission flow
|
||||
- FR-055: No IP/session tracking stored
|
||||
- FR-059, FR-060: File security
|
||||
- FR-061: Rate limiting
|
||||
|
||||
### Side Effects
|
||||
|
||||
1. **File System**:
|
||||
- Creates `data/products/{product_id}/feedback/{feedback_id}/`
|
||||
- Writes `metadata.yaml`, `content.txt`
|
||||
- Writes `attachments/{filename}` if files uploaded
|
||||
|
||||
2. **Async Processing**:
|
||||
- Triggers AI analysis background job
|
||||
- Updates feedback status: `submitted` → `analyzing`
|
||||
|
||||
---
|
||||
|
||||
## POST /submit/{product_slug}/status/{feedback_id}
|
||||
|
||||
**Note**: This endpoint is OUT OF SCOPE for MVP. Feedback submission is fire-and-forget. Users cannot track submission status anonymously.
|
||||
|
||||
Future consideration: Anonymous status check via feedback ID (requires balancing anonymity with user experience).
|
||||
Reference in New Issue
Block a user