|
@@ -1,636 +1,187 @@
|
|
|
-# API Documentation
|
|
|
|
|
|
|
+# API Overview
|
|
|
|
|
|
|
|
-All endpoints (except `/health`) require Bearer authentication using the `Authorization` header:
|
|
|
|
|
|
|
+This project ships a **self-documenting REST API**. Each endpoint carries its own metadata (parameters, request body, responses, auth requirement) and an endpoint registry generates both browsable HTML and an OpenAPI 3.0 spec at runtime.
|
|
|
|
|
|
|
|
-```
|
|
|
|
|
-Authorization: Bearer <your-api-key>
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-## Shop Identifiers
|
|
|
|
|
-
|
|
|
|
|
-All shop-related endpoints support dual identifier access:
|
|
|
|
|
-
|
|
|
|
|
-- **Internal UUID**: Auto-generated unique identifier assigned by the system (e.g., `550e8400-e29b-41d4-a716-446655440000`)
|
|
|
|
|
-- **Custom UUID**: Optional custom identifier set by the user (e.g., `cf1a0652-d4cd-4e6f-85a0-87d203578c35`)
|
|
|
|
|
-
|
|
|
|
|
-You can use either identifier in any shop endpoint URL parameter. Custom UUIDs are optional and must be in valid UUID format if provided.
|
|
|
|
|
-
|
|
|
|
|
-## Table of Contents
|
|
|
|
|
-
|
|
|
|
|
-- [Health Check](#health-check)
|
|
|
|
|
-- [Jobs](#jobs)
|
|
|
|
|
- - [Create Job](#create-job)
|
|
|
|
|
- - [Get Job Status](#get-job-status)
|
|
|
|
|
- - [List All Jobs](#list-all-jobs)
|
|
|
|
|
-- [Shops](#shops)
|
|
|
|
|
- - [List All Shops](#list-all-shops)
|
|
|
|
|
- - [Get Shop Details](#get-shop-details)
|
|
|
|
|
- - [Get Shop Results](#get-shop-results)
|
|
|
|
|
- - [Set/Update Custom ID](#setupdate-custom-id)
|
|
|
|
|
- - [Enable/Disable Schedule](#enabledisable-schedule)
|
|
|
|
|
- - [Delete Shop](#delete-shop)
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## Health Check
|
|
|
|
|
-
|
|
|
|
|
-Check the health status of the application.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `GET /health`
|
|
|
|
|
-
|
|
|
|
|
-**Authentication:** Not required
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "status": "ok",
|
|
|
|
|
- "timestamp": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "queue_stats": {
|
|
|
|
|
- "total": 10,
|
|
|
|
|
- "pending": 2,
|
|
|
|
|
- "running": 1,
|
|
|
|
|
- "completed": 6,
|
|
|
|
|
- "failed": 1
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## Jobs
|
|
|
|
|
-
|
|
|
|
|
-### Create Job
|
|
|
|
|
-
|
|
|
|
|
-Create a new scraping job for a webshop.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `POST /api/jobs`
|
|
|
|
|
-
|
|
|
|
|
-**Request Body:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "url": "https://example-shop.com",
|
|
|
|
|
- "custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Fields:**
|
|
|
|
|
-- `url` (required) - The main URL of the webshop to scrape
|
|
|
|
|
-- `custom_id` (optional) - Custom UUID identifier for the shop (must be unique)
|
|
|
|
|
-
|
|
|
|
|
-**Response:** `201 Created`
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
|
|
|
- "shop_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "status": "pending",
|
|
|
|
|
- "message": "Job created successfully"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `400 Bad Request` - Missing or invalid URL, or invalid custom_id format
|
|
|
|
|
-- `409 Conflict` - custom_id is already in use
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Example:**
|
|
|
|
|
-```bash
|
|
|
|
|
-curl -X POST http://localhost:3000/api/jobs \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key" \
|
|
|
|
|
- -H "Content-Type: application/json" \
|
|
|
|
|
- -d '{"url": "https://example-shop.com"}'
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Get Job Status
|
|
|
|
|
-
|
|
|
|
|
-Get the status and result of a specific job.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `GET /api/jobs/:id`
|
|
|
|
|
|
|
+**Always treat the running server as the source of truth.** This file covers the concepts — auth, conventions, shape of the surface — and points you at the live reference for the full endpoint list.
|
|
|
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Job UUID
|
|
|
|
|
|
|
+## Live reference
|
|
|
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
|
|
|
- "status": "completed",
|
|
|
|
|
- "created_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "updated_at": "2025-01-01T12:05:00.000Z",
|
|
|
|
|
- "result": {
|
|
|
|
|
- "initial_sitemap": "https://example-shop.com/sitemap.xml",
|
|
|
|
|
- "shipping_informations": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/shipping",
|
|
|
|
|
- "content": "# Shipping Information\n\n..."
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "contacts": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/contact",
|
|
|
|
|
- "content": "# Contact Us\n\n..."
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "terms_of_conditions": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/terms",
|
|
|
|
|
- "content": "# Terms and Conditions\n\n..."
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "faq": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/faq",
|
|
|
|
|
- "content": "# FAQ\n\n..."
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Status Values:**
|
|
|
|
|
-- `pending` - Job is waiting in queue
|
|
|
|
|
-- `running` - Job is currently being processed
|
|
|
|
|
-- `completed` - Job finished successfully
|
|
|
|
|
-- `failed` - Job failed (includes `error` field)
|
|
|
|
|
|
|
+Once the server is running, three documentation endpoints are available (no auth required):
|
|
|
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `404 Not Found` - Job not found
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
|
|
+| Endpoint | Returns |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `GET /doc` | Interactive HTML documentation page |
|
|
|
|
|
+| `GET /doc/openapi` | OpenAPI 3.0 JSON specification |
|
|
|
|
|
+| `GET /doc/endpoints` | Compact JSON list of `{ method, path, summary, tags, requiresAuth }` |
|
|
|
|
|
|
|
|
-**Example:**
|
|
|
|
|
```bash
|
|
```bash
|
|
|
-curl http://localhost:3000/api/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### List All Jobs
|
|
|
|
|
-
|
|
|
|
|
-Get a list of all jobs with queue statistics.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `GET /api/jobs`
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "stats": {
|
|
|
|
|
- "total": 10,
|
|
|
|
|
- "pending": 2,
|
|
|
|
|
- "running": 1,
|
|
|
|
|
- "completed": 6,
|
|
|
|
|
- "failed": 1
|
|
|
|
|
- },
|
|
|
|
|
- "jobs": [
|
|
|
|
|
- {
|
|
|
|
|
- "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
|
|
|
- "status": "completed",
|
|
|
|
|
- "sitemap_url": "https://example-shop.com",
|
|
|
|
|
- "created_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "updated_at": "2025-01-01T12:05:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
|
|
+# Browse in a browser:
|
|
|
|
|
+open http://localhost:3000/doc
|
|
|
|
|
|
|
|
-**Example:**
|
|
|
|
|
-```bash
|
|
|
|
|
-curl http://localhost:3000/api/jobs \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
|
|
+# Pipe the spec into anything OpenAPI-aware:
|
|
|
|
|
+curl http://localhost:3000/doc/openapi | jq .
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## Shops
|
|
|
|
|
-
|
|
|
|
|
-### List All Shops
|
|
|
|
|
|
|
+## Authentication
|
|
|
|
|
|
|
|
-Get a list of all shops with their analytics.
|
|
|
|
|
|
|
+All `/api/*` endpoints require **Bearer token authentication**. A handful of non-`/api` endpoints (`/health`, `/doc*`, `/ui/*`, `/mcp*`) are public.
|
|
|
|
|
|
|
|
-**Endpoint:** `GET /api/shops`
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "shops": [
|
|
|
|
|
- {
|
|
|
|
|
- "id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35",
|
|
|
|
|
- "url": "https://example-shop.com",
|
|
|
|
|
- "sitemap_url": "https://example-shop.com/sitemap.xml",
|
|
|
|
|
- "webshop_type": "shopify",
|
|
|
|
|
- "created_at": "2025-01-01T10:00:00.000Z",
|
|
|
|
|
- "updated_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "analytics": {
|
|
|
|
|
- "total_scrapes": 5,
|
|
|
|
|
- "last_scraped_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "next_scrape_at": "2025-01-02T12:00:00.000Z",
|
|
|
|
|
- "total_urls_found": 42,
|
|
|
|
|
- "average_scrape_time": 5432.5,
|
|
|
|
|
- "average_page_scrape_time": 234.8
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
-}
|
|
|
|
|
|
|
+```http
|
|
|
|
|
+Authorization: Bearer <API_KEY>
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
|
|
+The token must match the `API_KEY` environment variable. Missing or malformed `Authorization` headers return `401`. See `src/api/middleware/auth.ts`.
|
|
|
|
|
|
|
|
-**Example:**
|
|
|
|
|
```bash
|
|
```bash
|
|
|
curl http://localhost:3000/api/shops \
|
|
curl http://localhost:3000/api/shops \
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Get Shop Details
|
|
|
|
|
-
|
|
|
|
|
-Get detailed information about a specific shop including analytics, content metadata, scrape history, and scheduled jobs.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `GET /api/shops/:id`
|
|
|
|
|
-
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Shop identifier (can use either internal UUID or custom_id)
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "shop": {
|
|
|
|
|
- "id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35",
|
|
|
|
|
- "url": "https://example-shop.com",
|
|
|
|
|
- "sitemap_url": "https://example-shop.com/sitemap.xml",
|
|
|
|
|
- "webshop_type": "shopify",
|
|
|
|
|
- "created_at": "2025-01-01T10:00:00.000Z",
|
|
|
|
|
- "updated_at": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- },
|
|
|
|
|
- "analytics": {
|
|
|
|
|
- "total_scrapes": 5,
|
|
|
|
|
- "last_scraped_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "next_scrape_at": "2025-01-02T12:00:00.000Z",
|
|
|
|
|
- "total_urls_found": 42,
|
|
|
|
|
- "average_scrape_time": 5432.5,
|
|
|
|
|
- "average_page_scrape_time": 234.8
|
|
|
|
|
- },
|
|
|
|
|
- "content_metadata": {
|
|
|
|
|
- "shipping_informations": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/shipping",
|
|
|
|
|
- "changed": false,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "contacts": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/contact",
|
|
|
|
|
- "changed": true,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "terms_of_conditions": [],
|
|
|
|
|
- "faq": []
|
|
|
|
|
- },
|
|
|
|
|
- "scrape_history": [
|
|
|
|
|
- {
|
|
|
|
|
- "id": "history-uuid-1",
|
|
|
|
|
- "job_id": "job-uuid-1",
|
|
|
|
|
- "started_at": "2025-01-01T12:00:00.000Z",
|
|
|
|
|
- "completed_at": "2025-01-01T12:05:00.000Z",
|
|
|
|
|
- "status": "completed",
|
|
|
|
|
- "error": null,
|
|
|
|
|
- "scrape_time_ms": 5432,
|
|
|
|
|
- "urls_found": 42
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "scheduled_jobs": [
|
|
|
|
|
- {
|
|
|
|
|
- "id": "schedule-uuid-1",
|
|
|
|
|
- "next_run_at": "2025-01-02T12:00:00.000Z",
|
|
|
|
|
- "frequency": "daily",
|
|
|
|
|
- "last_modified": "2025-01-01T10:00:00.000Z",
|
|
|
|
|
- "status": "pending",
|
|
|
|
|
- "enabled": true,
|
|
|
|
|
- "created_at": "2025-01-01T10:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Notes:**
|
|
|
|
|
-- This endpoint returns content metadata only (URLs, change status, last updated)
|
|
|
|
|
-- To get full content, use the `/api/shops/:id/results` endpoint
|
|
|
|
|
-- `changed` flag indicates if content has changed since the previous scrape
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `404 Not Found` - Shop not found
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Example:**
|
|
|
|
|
-```bash
|
|
|
|
|
-curl http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000 \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Get Shop Results
|
|
|
|
|
-
|
|
|
|
|
-Get shop scraping results with full content. Supports filtering by date range, content type, and limit.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `GET /api/shops/:id/results`
|
|
|
|
|
-
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Shop identifier (can use either internal UUID or custom_id)
|
|
|
|
|
-
|
|
|
|
|
-**Query Parameters:**
|
|
|
|
|
-- `limit` (optional) - Maximum number of results to return
|
|
|
|
|
-- `date_from` (optional) - Filter results from this date (ISO 8601 format)
|
|
|
|
|
-- `date_to` (optional) - Filter results to this date (ISO 8601 format)
|
|
|
|
|
-- `content_type` (optional) - Filter by content type: `shipping`, `contacts`, `terms`, or `faq`
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "shop_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "filters": {
|
|
|
|
|
- "limit": 10,
|
|
|
|
|
- "date_from": "2025-01-01",
|
|
|
|
|
- "date_to": null,
|
|
|
|
|
- "content_type": null
|
|
|
|
|
- },
|
|
|
|
|
- "results": {
|
|
|
|
|
- "shipping_informations": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/shipping",
|
|
|
|
|
- "content": "# Shipping Information\n\nDetailed shipping content...",
|
|
|
|
|
- "changed": false,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/delivery-info",
|
|
|
|
|
- "content": "# Delivery Information\n\nDetailed delivery content...",
|
|
|
|
|
- "changed": true,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "contacts": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/contact",
|
|
|
|
|
- "content": "# Contact Us\n\nEmail: contact@example.com\nPhone: +1234567890",
|
|
|
|
|
- "changed": true,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "terms_of_conditions": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/terms",
|
|
|
|
|
- "content": "# Terms and Conditions\n\nDetailed terms...",
|
|
|
|
|
- "changed": false,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- "faq": [
|
|
|
|
|
- {
|
|
|
|
|
- "url": "https://example-shop.com/faq",
|
|
|
|
|
- "content": "# Frequently Asked Questions\n\nQ: Question 1?\nA: Answer 1...",
|
|
|
|
|
- "changed": false,
|
|
|
|
|
- "last_updated": "2025-01-01T12:00:00.000Z"
|
|
|
|
|
- }
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Notes:**
|
|
|
|
|
-- All content categories use arrays to support multiple pages per category
|
|
|
|
|
-- `changed` flag indicates if content has changed since the previous scrape
|
|
|
|
|
-- Content is in Markdown format, converted from HTML
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `404 Not Found` - Shop not found
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Examples:**
|
|
|
|
|
-
|
|
|
|
|
-Get all results:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl "http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/results" \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Get limited results:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl "http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/results?limit=10" \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Get results from a specific date:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl "http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/results?date_from=2025-01-01" \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Get only shipping information:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl "http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/results?content_type=shipping" \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Get results with date range and limit:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl "http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/results?date_from=2025-01-01&date_to=2025-01-31&limit=50" \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Set/Update Custom ID
|
|
|
|
|
-
|
|
|
|
|
-Set or update the custom ID for a shop.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `PATCH /api/shops/:id/custom-id`
|
|
|
|
|
-
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Shop identifier (can use either internal UUID or custom_id)
|
|
|
|
|
-
|
|
|
|
|
-**Request Body:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Fields:**
|
|
|
|
|
-- `custom_id` - Custom UUID identifier for the shop (set to `null` to remove)
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "shop_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35",
|
|
|
|
|
- "message": "Custom ID set to cf1a0652-d4cd-4e6f-85a0-87d203578c35"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `400 Bad Request` - Invalid custom_id format
|
|
|
|
|
-- `404 Not Found` - Shop not found
|
|
|
|
|
-- `409 Conflict` - custom_id is already in use
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Example:**
|
|
|
|
|
-```bash
|
|
|
|
|
-curl -X PATCH http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/custom-id \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key" \
|
|
|
|
|
- -H "Content-Type: application/json" \
|
|
|
|
|
- -d '{"custom_id": "cf1a0652-d4cd-4e6f-85a0-87d203578c35"}'
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Enable/Disable Schedule
|
|
|
|
|
-
|
|
|
|
|
-Enable or disable scheduled scraping for a shop.
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `PATCH /api/shops/:id/schedule`
|
|
|
|
|
-
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Shop identifier (can use either internal UUID or custom_id)
|
|
|
|
|
-
|
|
|
|
|
-**Request Body:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "enabled": true
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "shop_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
|
|
|
- "schedule_enabled": true,
|
|
|
|
|
- "message": "Schedule enabled successfully"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `400 Bad Request` - Missing or invalid `enabled` field
|
|
|
|
|
-- `404 Not Found` - Shop not found
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Examples:**
|
|
|
|
|
-
|
|
|
|
|
-Enable scheduling:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl -X PATCH http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/schedule \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key" \
|
|
|
|
|
- -H "Content-Type: application/json" \
|
|
|
|
|
- -d '{"enabled": true}'
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-Disable scheduling:
|
|
|
|
|
-```bash
|
|
|
|
|
-curl -X PATCH http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000/schedule \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key" \
|
|
|
|
|
- -H "Content-Type: application/json" \
|
|
|
|
|
- -d '{"enabled": false}'
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-### Delete Shop
|
|
|
|
|
-
|
|
|
|
|
-Delete a shop and all related data (analytics, scrape history, content, scheduled jobs).
|
|
|
|
|
-
|
|
|
|
|
-**Endpoint:** `DELETE /api/shops/:id`
|
|
|
|
|
-
|
|
|
|
|
-**URL Parameters:**
|
|
|
|
|
-- `id` - Shop identifier (can use either internal UUID or custom_id)
|
|
|
|
|
-
|
|
|
|
|
-**Response:**
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "message": "Shop and all related data deleted successfully",
|
|
|
|
|
- "shop_id": "550e8400-e29b-41d4-a716-446655440000"
|
|
|
|
|
-}
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**Notes:**
|
|
|
|
|
-- This operation is irreversible
|
|
|
|
|
-- Deletes all associated data:
|
|
|
|
|
- - Shop record
|
|
|
|
|
- - Analytics
|
|
|
|
|
- - Scrape history
|
|
|
|
|
- - All content records
|
|
|
|
|
- - Scheduled jobs
|
|
|
|
|
-
|
|
|
|
|
-**Error Responses:**
|
|
|
|
|
-- `404 Not Found` - Shop not found
|
|
|
|
|
-- `503 Service Unavailable` - Database not available
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-
|
|
|
|
|
-**Example:**
|
|
|
|
|
-```bash
|
|
|
|
|
-curl -X DELETE http://localhost:3000/api/shops/550e8400-e29b-41d4-a716-446655440000 \
|
|
|
|
|
- -H "Authorization: Bearer your-secret-key"
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## Error Handling
|
|
|
|
|
-
|
|
|
|
|
-All endpoints return standard HTTP status codes:
|
|
|
|
|
-
|
|
|
|
|
-- `200 OK` - Request successful
|
|
|
|
|
-- `201 Created` - Resource created successfully
|
|
|
|
|
-- `400 Bad Request` - Invalid request parameters
|
|
|
|
|
-- `404 Not Found` - Resource not found
|
|
|
|
|
-- `500 Internal Server Error` - Server error
|
|
|
|
|
-- `503 Service Unavailable` - Service (e.g., database) unavailable
|
|
|
|
|
-
|
|
|
|
|
-Error responses include a JSON body with an `error` field:
|
|
|
|
|
|
|
+ -H "Authorization: Bearer $API_KEY"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Endpoint surface (high-level)
|
|
|
|
|
+
|
|
|
|
|
+The registry is composed from endpoint components under `src/api/components/`. Grouped by tag:
|
|
|
|
|
+
|
|
|
|
|
+| Tag | Component | What it covers |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| Health | `HealthEndpoint.ts` | `/health` — public liveness + queue stats |
|
|
|
|
|
+| Jobs | `JobsEndpoint.ts` | Create and inspect scraping jobs |
|
|
|
|
|
+| Shops | `ShopsEndpoint.ts` | CRUD + analytics + schedule toggle + custom_id + Qdrant flag |
|
|
|
|
|
+| Content | `ContentEndpoint.ts` | Per-shop scraped content (enable/disable pages) |
|
|
|
|
|
+| Custom URLs | `CustomUrlsEndpoint.ts` | Manually add URLs the sitemap crawler would miss |
|
|
|
|
|
+| Webhooks | `WebhooksEndpoint.ts` | Per-shop webhook CRUD (scrape_started / completed / failed) |
|
|
|
|
|
+| Qdrant | `QdrantEndpoint.ts` | Vector DB management, per-shop embedding lifecycle, v1→v2 migration |
|
|
|
|
|
+| MCP | `McpEndpoint.ts` | MCP call logs and analytics |
|
|
|
|
|
+| Documentation | `DocumentationEndpoint.ts` | `/doc`, `/doc/openapi`, `/doc/endpoints` |
|
|
|
|
|
+
|
|
|
|
|
+Use `GET /doc/endpoints` for the exact, always-up-to-date list. A summarised snapshot at the time of writing:
|
|
|
|
|
+
|
|
|
|
|
+### Public
|
|
|
|
|
+- `GET /health`
|
|
|
|
|
+- `GET /doc`, `GET /doc/openapi`, `GET /doc/endpoints`
|
|
|
|
|
+- `POST /mcp`, `GET /mcp`, `DELETE /mcp`, `GET /mcp/health` *(only when MCP is enabled)*
|
|
|
|
|
+
|
|
|
|
|
+### Jobs
|
|
|
|
|
+- `POST /api/jobs` — create a scraping job from a URL
|
|
|
|
|
+- `GET /api/jobs` — list jobs with queue stats
|
|
|
|
|
+- `GET /api/jobs/:id` — job status and result
|
|
|
|
|
+
|
|
|
|
|
+### Shops
|
|
|
|
|
+- `GET /api/shops` — list with analytics
|
|
|
|
|
+- `GET /api/shops/deleted` — soft-deleted shops
|
|
|
|
|
+- `GET /api/shops/:id` — detail, supports either internal UUID or `custom_id`
|
|
|
|
|
+- `GET /api/shops/:id/results` — scraped content with filters
|
|
|
|
|
+- `PATCH /api/shops/:id/schedule` — enable/disable scheduled scraping
|
|
|
|
|
+- `PATCH /api/shops/:id/custom-id` — set/update custom_id
|
|
|
|
|
+- `PATCH /api/shops/:id/qdrant` — enable/disable embeddings (also see `PUT /api/shops/:shopId/qdrant/toggle`)
|
|
|
|
|
+- `DELETE /api/shops/:id` — soft delete
|
|
|
|
|
+- `DELETE /api/shops/:id/force` — hard delete
|
|
|
|
|
+
|
|
|
|
|
+### Shop content
|
|
|
|
|
+- `GET /api/shops/:id/content`
|
|
|
|
|
+- `PATCH /api/shops/:id/content/:contentId`
|
|
|
|
|
+
|
|
|
|
|
+### Shop custom URLs
|
|
|
|
|
+- `POST /api/shops/:id/custom-urls`
|
|
|
|
|
+- `GET /api/shops/:id/custom-urls`
|
|
|
|
|
+- `PATCH /api/shops/:id/custom-urls/:customUrlId`
|
|
|
|
|
+- `DELETE /api/shops/:id/custom-urls/:customUrlId`
|
|
|
|
|
+
|
|
|
|
|
+### Shop webhooks
|
|
|
|
|
+- `POST /api/shops/:id/webhooks`
|
|
|
|
|
+- `GET /api/shops/:id/webhooks`
|
|
|
|
|
+- `PATCH /api/shops/:id/webhooks`
|
|
|
|
|
+- `DELETE /api/shops/:id/webhooks`
|
|
|
|
|
+
|
|
|
|
|
+### Qdrant (system-wide)
|
|
|
|
|
+- `GET /api/qdrant/stats`
|
|
|
|
|
+- `GET /api/qdrant/health`
|
|
|
|
|
+- `GET /api/qdrant/collections`
|
|
|
|
|
+- `DELETE /api/qdrant/collections/:collectionName`
|
|
|
|
|
+- `POST /api/qdrant/cleanup`
|
|
|
|
|
+
|
|
|
|
|
+### Qdrant (per shop)
|
|
|
|
|
+- `GET /api/qdrant/shops/:shopId`
|
|
|
|
|
+- `GET /api/qdrant/shops/:shopId/embeddings`
|
|
|
|
|
+- `GET /api/qdrant/shops/:shopId/errors`
|
|
|
|
|
+- `DELETE /api/qdrant/shops/:shopId/errors`
|
|
|
|
|
+- `POST /api/qdrant/shops/:shopId/schedule-deletion`
|
|
|
|
|
+- `GET /api/shops/:shopId/qdrant` — detailed Qdrant state
|
|
|
|
|
+- `PUT /api/shops/:shopId/qdrant/toggle` — enable/disable
|
|
|
|
|
+- `PUT /api/shops/:shopId/qdrant/custom-id` — set immutable custom id for the shop
|
|
|
|
|
+- `POST /api/shops/:shopId/qdrant/re-embed`
|
|
|
|
|
+- `POST /api/shops/:shopId/qdrant/sync`
|
|
|
|
|
+- `GET /api/shops/:shopId/qdrant/errors`
|
|
|
|
|
+- `DELETE /api/shops/:shopId/qdrant/errors`
|
|
|
|
|
+- `DELETE /api/shops/:shopId/qdrant/pending-embeddings`
|
|
|
|
|
+
|
|
|
|
|
+### Qdrant migration (v1 legacy → v2 consolidated)
|
|
|
|
|
+- `GET /api/shops/:shopId/qdrant/migration-status`
|
|
|
|
|
+- `POST /api/shops/:shopId/qdrant/migrate`
|
|
|
|
|
+- `POST /api/shops/:shopId/qdrant/cleanup-old-collections`
|
|
|
|
|
+
|
|
|
|
|
+### MCP logs & analytics
|
|
|
|
|
+- `GET /api/mcp/stats`
|
|
|
|
|
+- `GET /api/mcp/analytics`
|
|
|
|
|
+- `GET /api/mcp/logs`
|
|
|
|
|
+- `GET /api/mcp/logs/:logId`
|
|
|
|
|
+- `DELETE /api/mcp/logs/cleanup`
|
|
|
|
|
+- `GET /api/mcp/tools/stats`
|
|
|
|
|
+- `GET /api/mcp/shops/:shopId/logs`
|
|
|
|
|
+- `DELETE /api/mcp/shops/:shopId/logs`
|
|
|
|
|
+- `GET /api/shops/:shopId/mcp/analytics`
|
|
|
|
|
+
|
|
|
|
|
+For every endpoint above, `/doc/openapi` returns the full request/response schema.
|
|
|
|
|
+
|
|
|
|
|
+## Shop identifiers
|
|
|
|
|
+
|
|
|
|
|
+Every shop endpoint path parameter `:id` accepts **either**:
|
|
|
|
|
+
|
|
|
|
|
+- the internal UUID generated by the system, or
|
|
|
|
|
+- the `custom_id` if one is set.
|
|
|
|
|
+
|
|
|
|
|
+The lookup in `src/database/Database.ts` tries both. `custom_id` must be a valid UUID and becomes immutable once Qdrant is enabled for the shop.
|
|
|
|
|
+
|
|
|
|
|
+## Content categories
|
|
|
|
|
+
|
|
|
|
|
+Scraped pages are classified into four buckets, used consistently across the API, the DB, and the MCP tools:
|
|
|
|
|
+
|
|
|
|
|
+- `shipping` — shipping, delivery, logistics
|
|
|
|
|
+- `contacts` — contact info, support
|
|
|
|
|
+- `terms` — terms of service, privacy, legal
|
|
|
|
|
+- `faq` — frequently asked questions, help pages
|
|
|
|
|
+
|
|
|
|
|
+## Error responses
|
|
|
|
|
+
|
|
|
|
|
+Errors return a JSON body:
|
|
|
|
|
|
|
|
```json
|
|
```json
|
|
|
-{
|
|
|
|
|
- "error": "Description of the error"
|
|
|
|
|
-}
|
|
|
|
|
|
|
+{ "error": "Description of the error" }
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-## Content Types
|
|
|
|
|
|
|
+Status codes follow standard REST semantics: `400` for bad input, `401` for bad auth, `404` for missing resources, `409` for conflicts (e.g. duplicate `custom_id`), `500` for server errors, `503` when the database or Qdrant is unavailable.
|
|
|
|
|
|
|
|
-The scraper categorizes content into four types:
|
|
|
|
|
|
|
+## MCP (Model Context Protocol)
|
|
|
|
|
|
|
|
-1. **shipping** (shipping_informations) - Shipping, delivery, and logistics information
|
|
|
|
|
-2. **contacts** - Contact information (email, phone, addresses)
|
|
|
|
|
-3. **terms** (terms_of_conditions) - Terms of service, privacy policy, legal information
|
|
|
|
|
-4. **faq** - Frequently asked questions and help pages
|
|
|
|
|
|
|
+When both `MCP_ENABLED=true` and `QDRANT_ENABLED=true`, the server mounts a **Streamable HTTP** MCP endpoint at `/mcp` (see `src/mcp/McpServer.ts`). Five tools are registered:
|
|
|
|
|
|
|
|
-Each category can contain multiple pages (returned as arrays).
|
|
|
|
|
|
|
+| Tool name | Purpose |
|
|
|
|
|
+|---|---|
|
|
|
|
|
+| `list_contents` | Enumerate available pages for a shop, grouped by category, each tagged with the search tool to use next. Call this first. |
|
|
|
|
|
+| `shipping_search` | Semantic search in shipping/delivery pages |
|
|
|
|
|
+| `contacts_search` | Semantic search in contact/support pages |
|
|
|
|
|
+| `terms_search` | Semantic search in terms/policy pages |
|
|
|
|
|
+| `faq_search` | Semantic search in FAQ/help pages |
|
|
|
|
|
|
|
|
-## Scheduled Scraping
|
|
|
|
|
|
|
+All tools require `shop_id` (the shop's `custom_id`). The four search tools additionally take `query` (required) and `limit` (optional, 1–20, default 10). If a shop does not exist or does not have Qdrant enabled, tools return `"Search not available for this shop"` rather than erroring.
|
|
|
|
|
|
|
|
-The application automatically schedules scraping based on sitemap rules:
|
|
|
|
|
|
|
+Tool schemas are declared in `src/mcp/tools/*.ts` and exposed to MCP clients via the standard `tools/list` request.
|
|
|
|
|
|
|
|
-- **Frequency rules** from sitemap: `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`
|
|
|
|
|
-- **Last modified dates** from sitemap
|
|
|
|
|
-- Schedules can be enabled/disabled per shop
|
|
|
|
|
-- Scheduler checks for due jobs every minute
|
|
|
|
|
-- After each successful scrape, the next scrape is automatically scheduled
|
|
|
|
|
|
|
+## Response compression
|
|
|
|
|
|
|
|
-## Content Change Detection
|
|
|
|
|
|
|
+All responses larger than 1 KB are automatically gzip/brotli-compressed by the `compression` middleware (`src/api/server.ts`). Set header `x-no-compression: 1` to opt out.
|
|
|
|
|
|
|
|
-The system uses MD5 hashing to detect content changes:
|
|
|
|
|
|
|
+## What this document deliberately does not include
|
|
|
|
|
|
|
|
-- Each content piece is hashed when scraped
|
|
|
|
|
-- Compared with the previous version's hash
|
|
|
|
|
-- `changed` flag set to `true` if hashes differ
|
|
|
|
|
-- Change detection works for all content types
|
|
|
|
|
-- Useful for monitoring when shop policies or information are updated
|
|
|
|
|
|
|
+- Full request/response JSON schemas — they live in `/doc/openapi`, generated from the endpoint metadata. Duplicating them here guarantees drift.
|
|
|
|
|
+- Rate limiting — the server does not implement any; if you put one in, document it here.
|
|
|
|
|
+- An SDK — there isn't one. Use any HTTP client with the Bearer token.
|