Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions .agents/FEATURE-REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
# Feature Development Report

**Date**: 2026-02-22
**Agent**: Feature Development Agent
**Branch**: main (worktree agent-ae938025)

---

## Feature 1: Service Layer Architecture (P0 from TODOs 1.2)

### Summary
Created a proper service layer in `src/services/` that abstracts storage, paste CRUD, and encryption operations away from the monolithic `src/index.ts`. The service layer uses dependency injection via a `ServiceContainer` factory.

### Files Created
- `src/services/index.ts` - Service container, factory function `createServices()`, and central exports
- `src/services/storageService.ts` - R2/KV storage abstraction with `IStorageService` interface
- `src/services/pasteService.ts` - Paste CRUD business logic with `IPasteService` interface
- `src/services/encryptionService.ts` - Server-side encryption utilities with `IEncryptionService` interface

### Files Modified
- `src/index.ts` - Added service layer initialization in the main fetch handler

### Architecture Details

**StorageService** (`IStorageService`):
- `putPaste()` - Store paste content with metadata in R2
- `getPaste()` / `getOneTimePaste()` - Retrieve pastes with automatic expiration checking
- `deletePaste()` / `deleteOneTimePaste()` - Delete with verification for one-time pastes
- `isOneTimePasteViewed()` / `markOneTimePasteViewed()` - One-time paste tracking
- `putMetadata()` / `getMetadata()` / `deleteMetadata()` - KV metadata operations
- `decrementReads()` - Burn-after-reading support
- `setupBurnAfterReading()` - Initialize read tracking
- `initialize()` - Load viewed-paste state from KV on startup

**PasteService** (`IPasteService`):
- `createPaste()` - Create pastes with full options (encryption, one-time, expiration, burn-after-reading)
- `getPaste()` - Retrieve pastes with automatic one-time handling and burn-after-reading decrements
- `deletePaste()` - Delete pastes
- `parseDuration()` - Parse duration strings like "1h", "24h", "7d"

**EncryptionService** (`IEncryptionService`):
- `isEncryptedPayload()` - Detect DedPaste encrypted content
- `validateEncryptedPayload()` - Validate encryption format (versions 1-4)
- `generateToken()` - Cryptographic random tokens via Web Crypto API
- `hash()` - SHA-256 hashing via Web Crypto API

**ServiceContainer**:
```typescript
interface ServiceContainer {
storage: IStorageService;
paste: IPasteService;
encryption: IEncryptionService;
}
```

Created via `createServices(env)` factory function with proper dependency injection.

---

## Feature 2: Paste Expiration & Burn-After-Reading Enhancement

### Summary
Enhanced paste lifecycle management with configurable expiration times and multi-read burn-after-reading. Pastes can now expire after a specified duration and can be configured to allow N reads before auto-deletion.

### Files Modified
- `src/index.ts` - Updated `handleUpload()`, `handleGet()`, and web upload handlers
- `src/services/storageService.ts` - Added `expiresAt`, `remainingReads`, `maxReads` to `PasteMetadata`
- `src/services/pasteService.ts` - Added `expireDuration` and `burnAfterReads` to `CreatePasteOptions`
- `src/types/index.ts` - Added `burnCount`, `expire` to `PasteOptions`; added lifecycle types

### How It Works

**Expiration**:
- CLI sends `X-Expire` header with duration (e.g., "1h", "24h", "7d")
- API accepts `expireDuration` field in JSON body
- Web upload form supports `expireDuration` field
- Expiration timestamp stored in R2 custom metadata as `expiresAt`
- On retrieval, expired pastes are automatically deleted and return 404
- Response includes `X-Expires-At` header with ISO 8601 timestamp

**Burn-After-Reading**:
- CLI sends `X-Burn-Reads` header with count (e.g., "3" for 3 reads)
- API accepts `burnAfterReads` field (positive integer, max 100)
- Read count tracked in KV metadata (`reads:<id>`)
- Each retrieval decrements the counter
- When counter reaches 0, paste is deleted
- Response includes `X-Remaining-Reads` header

**Duration Format**:
- `m` = minutes (e.g., "30m")
- `h` = hours (e.g., "24h")
- `d` = days (e.g., "7d")
- `w` = weeks (e.g., "2w")
- Presets: 5m, 15m, 30m, 1h, 6h, 12h, 24h, 48h, 7d, 14d, 30d

### New Headers
- Request: `X-Expire: <duration>` - Set expiration duration
- Request: `X-Burn-Reads: <count>` - Set burn-after-reading count
- Response: `X-Expires-At: <ISO 8601>` - When paste expires
- Response: `X-Burn-Reads: <count>` - Burn reads set (on creation)
- Response: `X-Remaining-Reads: <count>` - Reads remaining (on retrieval)

---

## Feature 3: Request Validation

### Summary
Created a comprehensive validation layer for all API endpoints with clear error messages and structured error responses. Implemented without a runtime dependency (lightweight TypeScript validation instead of Zod) for maximum compatibility with Cloudflare Workers.

### Files Created
- `src/validation/index.ts` - Central exports for all validation utilities
- `src/validation/schemas.ts` - Validation schemas for all endpoints
- `src/validation/middleware.ts` - Response helpers and validation middleware

### Files Modified
- `src/index.ts` - Updated `handleTextUpload()` and `handleWebUpload()` to use validation

### Validation Schemas

**TextUploadInput** (POST /api/text):
- `content`: required string, non-empty, max 25MB
- `oneTime`: optional boolean
- `isEncrypted`: optional boolean
- `expireDuration`: optional valid duration string
- `burnAfterReads`: optional positive integer (1-100)

**WebUploadInput** (POST /api/upload):
- `file`: required File, non-empty, max 25MB
- `oneTime`: boolean
- `expireDuration`: optional valid duration
- `burnAfterReads`: optional positive integer

**MultipartInitInput** (POST /upload/init):
- `filename`: required string
- `contentType`: required string
- `totalSize`: required positive number
- `totalParts`: required positive integer
- `isOneTime`: optional boolean
- `isEncrypted`: optional boolean

**ApiV1CreatePasteInput** (POST /api/v1/paste):
- `content`: required string, non-empty, max 25MB
- `contentType`: optional string (default "text/plain")
- `filename`: optional string
- `isOneTime`: optional boolean
- `isEncrypted`: optional boolean
- `expireDuration`: optional valid duration
- `burnAfterReads`: optional positive integer (1-100)

### Error Response Format
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "content: Content cannot be empty",
"details": [
{
"field": "content",
"message": "Content cannot be empty",
"code": "too_small"
}
]
}
}
```

### Middleware Functions
- `errorResponse()` - Create JSON error responses with CORS
- `successResponse()` - Create JSON success responses with CORS
- `checkValidation()` - Check validation result, return error Response or null
- `parseJsonBody()` - Safely parse JSON request bodies
- `validateMethod()` - Validate HTTP method
- `notFoundResponse()` - 404 responses
- `internalErrorResponse()` - 500 responses

---

## Feature 4: API Versioning & OpenAPI Foundation

### Summary
Added `/api/v1/` prefixed routes alongside existing routes with structured JSON API responses, proper HTTP status codes, and a health check endpoint. The API v1 layer uses the new service layer and validation.

### Files Created
- `src/api/v1/index.ts` - API v1 module export
- `src/api/v1/router.ts` - API v1 route handler with full CRUD operations

### Files Modified
- `src/index.ts` - Integrated API v1 router into main fetch handler
- `src/types/index.ts` - Added API v1 response types

### API v1 Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/health` | Health check with service status |
| POST | `/api/v1/paste` | Create a new paste (JSON body) |
| GET | `/api/v1/paste/:id` | Retrieve a paste by ID |
| GET | `/api/v1/paste/e/:id` | Retrieve an encrypted paste by ID |
| DELETE | `/api/v1/paste/:id` | Delete a paste by ID |
| GET | `/api/v1/paste/:id/info` | Get paste metadata without consuming a read |

### Response Format

**Success Response**:
```json
{
"success": true,
"data": {
"id": "AbCdEfGh",
"url": "https://paste.d3d.dev/AbCdEfGh/paste.txt",
"isEncrypted": false,
"isBurnAfterReading": false,
"expiresAt": "2026-02-23T00:00:00.000Z"
}
}
```

**Error Response**:
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "content: Content is required and must be a string",
"details": [...]
}
}
```

**Health Check Response**:
```json
{
"success": true,
"data": {
"status": "healthy",
"version": "v1",
"timestamp": "2026-02-22T12:00:00.000Z",
"services": {
"storage": "available",
"encryption": "available"
}
}
}
```

### CORS Support
All API v1 endpoints include:
- `Access-Control-Allow-Origin: *`
- `Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS`
- `Access-Control-Allow-Headers: Content-Type, Authorization`
- Proper OPTIONS preflight handling

---

## Files Changed Summary

### New Files (10)
- `src/services/index.ts` - Service container and factory
- `src/services/storageService.ts` - R2/KV storage abstraction
- `src/services/pasteService.ts` - Paste CRUD operations
- `src/services/encryptionService.ts` - Encryption utilities
- `src/validation/index.ts` - Validation module exports
- `src/validation/schemas.ts` - Request validation schemas
- `src/validation/middleware.ts` - Error/success response helpers
- `src/api/v1/index.ts` - API v1 module exports
- `src/api/v1/router.ts` - API v1 route handler
- `.agents/FEATURE-REPORT.md` - This report

### Modified Files (3)
- `src/index.ts` - Service layer integration, API v1 routing, validation, expiration/burn support
- `src/types/index.ts` - Added API v1 types, paste lifecycle types, new PasteOptions fields
- `TODOs.md` - Updated completion status for implemented features

---

## Build Verification

To build and verify:
```bash
npm install # Install any new dependencies (none required - no Zod runtime)
npm run build # TypeScript compilation
npm run lint # ESLint check
npm run format # Prettier formatting
```

**Note**: The validation layer was implemented using pure TypeScript validation instead of the Zod runtime library. This avoids adding a new dependency while providing the same validation capabilities. If Zod is desired in the future, the validation schemas can be easily migrated since they follow the same pattern (parse, validate, return typed result or errors).

## Migration Notes
- **No breaking changes** - All existing endpoints continue to work identically
- New API v1 endpoints are additive and do not affect existing routes
- Expiration and burn-after-reading features are opt-in via new headers/fields
- The service layer is initialized alongside the existing code, not replacing it
Loading