diff --git a/app/api/activities/readme.md b/app/api/activities/readme.md new file mode 100644 index 0000000..cfa2095 --- /dev/null +++ b/app/api/activities/readme.md @@ -0,0 +1,126 @@ +# Activities API Documentation + +## Overview +The Activities API provides event tracking data for short URLs. It allows retrieving visitor activity information based on various filters such as URL slug, domain, and date ranges. + +## Endpoint +``` +GET /api/activities +``` + +## Request Parameters + +| Parameter | Type | Required | Description | +|------------|---------|----------|-------------| +| `slug` | string | No* | The short URL slug to filter events by | +| `domain` | string | No* | The domain to filter events by | +| `startTime`| string | No* | Start time for date range filter (ISO format) | +| `endTime` | string | No* | End time for date range filter (ISO format) | +| `page` | integer | No | Page number for pagination (default: 1) | +| `pageSize` | integer | No | Number of records per page (default: 50) | +| `format` | string | No | Response format, set to 'csv' for CSV output (default: JSON) | + +\* Either `slug`+`domain` combination OR at least one of `startTime`/`endTime` must be provided. + +## Response Formats + +### JSON Format (Default) +JSON responses include the following structure: + +```json +{ + "success": true, + "data": [ + { + "id": "event-id", + "type": "event-type", + "time": "timestamp", + "visitor": { + "id": "visitor-id", + "ipAddress": "ip-address", + "userAgent": "user-agent-string", + "referrer": "referrer-url" + }, + "device": { + "type": "device-type", + "browser": "browser-name", + "os": "operating-system" + }, + "location": { + "country": "country-code", + "city": "city-name" + }, + "link": { + "id": "link-id", + "slug": "link-slug", + "originalUrl": "original-url", + "label": "link-label", + "tags": ["tag1", "tag2"] + }, + "utm": { + "source": "utm-source", + "medium": "utm-medium", + "campaign": "utm-campaign", + "term": "utm-term", + "content": "utm-content" + } + } + ], + "meta": { + "total": 100, + "page": 1, + "pageSize": 50 + } +} +``` + +In case of an error: +```json +{ + "success": false, + "data": null, + "error": "Error message description" +} +``` + +### CSV Format +When `format=csv` is specified, the response is returned as plain text in CSV format with the following columns: +- `time`: Timestamp of the event +- `activity`: Type of activity/event +- `campaign`: UTM campaign value (defaults to "demo" if not found) +- `clientId`: Visitor ID +- `originPath`: Original request path or referrer URL + +## Examples + +### Get activities for a specific short URL +``` +GET /api/activities?slug=promo123&domain=googleads.link +``` + +### Get activities within a date range +``` +GET /api/activities?startTime=2023-06-01T00:00:00Z&endTime=2023-06-30T23:59:59Z +``` + +### Get events as CSV +``` +GET /api/activities?slug=promo123&domain=googleads.link&format=csv +``` + +### Pagination example +``` +GET /api/activities?slug=promo123&domain=googleads.link&page=2&pageSize=20 +``` + +## Error Codes + +| Status Code | Description | +|-------------|-------------| +| 400 | Missing required parameters | +| 500 | Server error while processing the request | + +## Notes +- For privacy and security reasons, some fields may be omitted or anonymized based on user settings. +- The CSV format is optimized for easy import into spreadsheet applications. +- When using the CSV format, the response is returned as plain text rather than a downloadable file. diff --git a/app/api/activities/route.ts b/app/api/activities/route.ts index d62a426..69c8dfc 100644 --- a/app/api/activities/route.ts +++ b/app/api/activities/route.ts @@ -38,12 +38,19 @@ export async function GET(request: NextRequest) { const startTime = searchParams.get('startTime') || undefined; const endTime = searchParams.get('endTime') || undefined; - // 修改验证逻辑,允许只使用时间范围 - // 现在只需要确保有足够的过滤条件 + // Check if either slug or domain is provided without the other + if ((slug && !domain) || (!slug && domain)) { + return NextResponse.json({ + success: false, + error: 'Both slug and domain parameters must be provided together' + }, { status: 400 }); + } + + // Ensure either slug+domain or date range is provided if ((!slug && !domain) && (!startTime && !endTime)) { return NextResponse.json({ success: false, - error: 'Missing filter parameters: provide either slug/domain or date range' + error: 'Missing filter parameters: provide either slug+domain or date range' }, { status: 400 }); }