{
  "openapi": "3.1.0",
  "info": {
    "title": "Share Notes API",
    "version": "1.2.0",
    "description": "Next-gen website sharing platform. Submit URLs, search sites, real-time chat, AI translation.\n\n新世代网站分享平台。提交网址、搜索网站、实时聊天、AI 翻译。",
    "contact": {
      "name": "KANG",
      "email": "k@Share.Mini-Apps.net",
      "url": "https://Share.Mini-Apps.net/developers"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://Share.Mini-Apps.net",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/sites": {
      "get": {
        "operationId": "searchSites",
        "summary": "Search and browse sites",
        "description": "Search shared websites by keyword, filter by tag, sort by hot score or recency. Returns paginated results (60 per page).",
        "parameters": [
          { "name": "search", "in": "query", "schema": { "type": "string" }, "description": "Keyword search" },
          { "name": "tag", "in": "query", "schema": { "type": "string" }, "description": "Filter by category tag" },
          { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["hot", "recent"], "default": "hot" }, "description": "Sort order" },
          { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 }, "description": "Page number" }
        ],
        "responses": {
          "200": {
            "description": "Paginated site list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sites": { "type": "array", "items": { "$ref": "#/components/schemas/Site" } },
                    "total": { "type": "integer" },
                    "page": { "type": "integer" },
                    "hasMore": { "type": "boolean" }
                  }
                },
                "example": {
                  "sites": [{ "id": "abc123", "url": "https://example.com", "title": "Example", "tags": ["工具"], "hot_score": 5.2 }],
                  "total": 42,
                  "page": 1,
                  "hasMore": true
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "submitSite",
        "summary": "Submit a new URL",
        "description": "Submit a URL to the platform. Social profile URLs are auto-detected. AI generates tags asynchronously. Metadata is fetched server-side.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "description": "The URL to submit" },
                  "turnstileToken": { "type": "string", "description": "Cloudflare Turnstile CAPTCHA token (required when enabled)" }
                }
              },
              "example": { "url": "https://example.com" }
            }
          }
        },
        "responses": {
          "201": { "description": "New site created", "content": { "application/json": { "example": { "site": { "id": "abc123", "url": "https://example.com", "title": "", "tags": [] }, "isNew": true } } } },
          "200": { "description": "Existing site, submit count incremented", "content": { "application/json": { "example": { "site": { "id": "abc123" }, "isNew": false } } } },
          "400": { "description": "Invalid URL or request" },
          "403": { "description": "Invalid Turnstile token" },
          "429": { "description": "Rate limited (5/min per IP)" }
        }
      }
    },
    "/api/sites/tags": {
      "get": {
        "operationId": "getTags",
        "summary": "Get all category tags",
        "responses": {
          "200": {
            "description": "Tag list",
            "content": { "application/json": { "example": { "tags": ["工具", "技术", "设计", "新闻", "社区", "学习", "娱乐", "资源", "商业", "生活", "社交"] } } }
          }
        }
      }
    },
    "/api/sites/{id}/boost": {
      "post": {
        "operationId": "boostSite",
        "summary": "Boost a site's visibility",
        "description": "Increase a site's hot score. Free users: 1/day. Logged-in users: 3/day.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID" }
        ],
        "responses": {
          "200": { "description": "Site boosted", "content": { "application/json": { "example": { "site": { "id": "abc123", "boost_count": 5 }, "credits": 2 } } } },
          "404": { "description": "Site not found" },
          "429": { "description": "Daily limit reached" }
        }
      }
    },
    "/api/sites/{id}/claim": {
      "post": {
        "operationId": "claimSite",
        "summary": "Claim site ownership",
        "description": "Verify site ownership by checking for <meta name=\"sharenotes-verify\" content=\"{siteId}\"> in the site's HTML. Benefits: verified badge, click analytics, priority display.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID" }
        ],
        "responses": {
          "200": { "description": "Site claimed", "content": { "application/json": { "example": { "site": { "id": "abc123", "is_claimed": true }, "verified": true } } } },
          "400": { "description": "Meta tag not found or already claimed" },
          "404": { "description": "Site not found" }
        }
      }
    },
    "/api/clicks/{id}": {
      "post": {
        "operationId": "recordClick",
        "summary": "Record a click",
        "description": "Record a click for analytics. Rate limited to 30/min per IP.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID" }
        ],
        "responses": {
          "200": { "description": "Click recorded", "content": { "application/json": { "example": { "recorded": true } } } }
        }
      }
    },
    "/api/messages": {
      "get": {
        "operationId": "getMessages",
        "summary": "Get recent chat messages",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 100 }, "description": "Number of messages" }
        ],
        "responses": {
          "200": {
            "description": "Message list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": { "type": "array", "items": { "$ref": "#/components/schemas/ChatMessage" } }
                  }
                },
                "example": {
                  "messages": [{ "id": "msg1", "type": "chat", "content": "Hello!", "sender_name": "user123", "created_at": "2026-03-27T12:00:00Z" }]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "sendMessage",
        "summary": "Send a chat message",
        "description": "Send a message to the live chat. Max 200 characters. Rate limited to 10/min per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["content"], "properties": { "content": { "type": "string", "maxLength": 200 } } },
              "example": { "content": "Hello from the API!" }
            }
          }
        },
        "responses": {
          "201": { "description": "Message sent", "content": { "application/json": { "example": { "message": { "id": "msg1", "type": "chat", "content": "Hello from the API!", "sender_name": "United States Dallas", "sender_location": "US|Dallas", "created_at": "2026-03-27T12:00:00Z" } } } } },
          "400": { "description": "Empty or too long" },
          "429": { "description": "Rate limited" }
        }
      }
    },
    "/api/translate": {
      "post": {
        "operationId": "translateText",
        "summary": "AI-powered translation",
        "description": "Translate text between 12 languages using Cloudflare Workers AI (M2M100-1.2B). Source language is auto-detected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["text", "targetLang"],
                "properties": {
                  "text": { "type": "string", "maxLength": 2000 },
                  "targetLang": { "type": "string", "enum": ["zh-CN", "zh-HK", "zh-TW", "en", "es", "ar", "hi", "pt", "ja", "ko", "fr", "de"] }
                }
              },
              "example": { "text": "Hello world", "targetLang": "zh-CN" }
            }
          }
        },
        "responses": {
          "200": { "description": "Translation result", "content": { "application/json": { "example": { "translated": "你好世界" } } } },
          "400": { "description": "Missing params or text too long" },
          "429": { "description": "Rate limited (20/min per IP)" },
          "500": { "description": "AI translation failed" }
        }
      }
    },
    "/api/bing-bg": {
      "get": {
        "operationId": "getBingBackground",
        "summary": "Daily background image",
        "description": "Get the daily background image from Bing. Cached for 6 hours.",
        "responses": {
          "200": {
            "description": "Background image data",
            "content": {
              "application/json": {
                "example": { "imageUrl": "https://www.bing.com/th?id=...", "videoUrl": null, "copyright": "Photo credit", "title": "Beautiful landscape" }
              }
            }
          }
        }
      }
    },
    "/api/auth/send-code": {
      "post": {
        "operationId": "sendAuthCode",
        "summary": "Send login verification code",
        "description": "Send a 6-digit verification code to the provided email. Rate limited to 3/min per email.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } },
              "example": { "email": "user@example.com" }
            }
          }
        },
        "responses": {
          "200": { "description": "Code sent", "content": { "application/json": { "example": { "sent": true } } } },
          "400": { "description": "Invalid email" },
          "429": { "description": "Rate limited" }
        }
      }
    },
    "/api/auth/verify": {
      "post": {
        "operationId": "verifyAuthCode",
        "summary": "Verify code and create session",
        "description": "Verify the email code and create a session. Sets HttpOnly cookie sn_token with JWT.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["email", "code"], "properties": { "email": { "type": "string" }, "code": { "type": "string" } } },
              "example": { "email": "user@example.com", "code": "123456" }
            }
          }
        },
        "responses": {
          "200": { "description": "Session created", "content": { "application/json": { "example": { "user": { "id": "usr_abc", "email": "user@example.com", "username": "user_abc" } } } } },
          "400": { "description": "Invalid or expired code" },
          "429": { "description": "Rate limited" }
        }
      }
    },
    "/api/auth/set-username": {
      "post": {
        "operationId": "setUsername",
        "summary": "Change display name",
        "description": "Update the authenticated user's display name. Requires session cookie.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["username"], "properties": { "username": { "type": "string", "minLength": 2, "maxLength": 20 } } },
              "example": { "username": "new_name" }
            }
          }
        },
        "responses": {
          "200": { "description": "Username updated" },
          "401": { "description": "Not authenticated" }
        }
      }
    },
    "/api/bookmarks": {
      "get": {
        "operationId": "getBookmarks",
        "summary": "Get user bookmarks",
        "description": "Get the authenticated user's bookmarked sites. Requires session cookie.",
        "responses": {
          "200": { "description": "Bookmark list", "content": { "application/json": { "example": { "bookmarks": [{ "site_id": "abc123", "created_at": "2026-03-27T12:00:00Z" }] } } } },
          "401": { "description": "Not authenticated" }
        }
      },
      "post": {
        "operationId": "addBookmark",
        "summary": "Add bookmark",
        "description": "Bookmark a site. Requires session cookie.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["siteId"], "properties": { "siteId": { "type": "string" } } },
              "example": { "siteId": "abc123" }
            }
          }
        },
        "responses": {
          "201": { "description": "Bookmark added" },
          "401": { "description": "Not authenticated" }
        }
      }
    },
    "/api/bookmarks/{id}": {
      "delete": {
        "operationId": "removeBookmark",
        "summary": "Remove bookmark",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID" }
        ],
        "responses": {
          "200": { "description": "Bookmark removed" },
          "401": { "description": "Not authenticated" }
        }
      }
    },
    "/api/sites/my-submissions": {
      "get": {
        "operationId": "getMySubmissions",
        "summary": "Get current user's submitted sites",
        "description": "List sites submitted by the authenticated user. Requires session cookie.",
        "responses": {
          "200": { "description": "Submission list", "content": { "application/json": { "example": { "sites": [{ "id": "abc123", "url": "https://example.com", "title": "Example" }] } } } },
          "401": { "description": "Not authenticated" }
        }
      }
    },
    "/api/sites/{id}/promotion": {
      "get": {
        "operationId": "getSitePromotion",
        "summary": "Get site promotion status",
        "description": "Check if a site is currently promoted and whether it has been claimed.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Site ID" }
        ],
        "responses": {
          "200": { "description": "Promotion status", "content": { "application/json": { "example": { "is_promoted": false, "promoted_until": "", "is_claimed": true } } } },
          "404": { "description": "Site not found" }
        }
      }
    },
    "/api/ws": {
      "get": {
        "operationId": "websocketConnect",
        "summary": "WebSocket real-time connection",
        "description": "Upgrade to WebSocket for real-time chat and site updates. Events: new_message (ChatMessage), site_update (partial Site), like ({ messageId, fromUser, toUser }), crawler_visit. Powered by Cloudflare Durable Objects.",
        "responses": {
          "101": { "description": "WebSocket upgrade" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Site": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "description": "Unique ID (nanoid)" },
          "url": { "type": "string", "description": "Full URL" },
          "title": { "type": "string" },
          "description": { "type": "string" },
          "favicon_url": { "type": "string" },
          "og_image_url": { "type": "string" },
          "domain": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" }, "description": "AI-generated category tags" },
          "submit_count": { "type": "integer" },
          "click_count": { "type": "integer" },
          "hot_score": { "type": "number", "description": "Trending score" },
          "site_type": { "type": "string", "enum": ["website", "profile"] },
          "social_platform": { "type": "string", "description": "Platform name for profile-type sites" },
          "social_username": { "type": "string" },
          "boost_count": { "type": "integer" },
          "is_claimed": { "type": "boolean" },
          "is_promoted": { "type": "boolean" },
          "promoted_until": { "type": "string", "format": "date-time" },
          "first_submitted_at": { "type": "string", "format": "date-time" },
          "last_activity_at": { "type": "string", "format": "date-time" }
        }
      },
      "ChatMessage": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "type": { "type": "string", "enum": ["chat", "system", "entrance"] },
          "content": { "type": "string" },
          "content_key": { "type": "string", "description": "i18n key for system messages" },
          "content_params": { "type": "object", "description": "Interpolation params for i18n" },
          "sender_name": { "type": "string", "description": "Display name or geo-based name (English)" },
          "sender_location": { "type": "string", "description": "Raw location e.g. CN|Beijing" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      }
    }
  },
  "externalDocs": {
    "description": "LLM-friendly API summary",
    "url": "https://Share.Mini-Apps.net/llms.txt"
  }
}
