From 142032462ac0685914d7bbd6b008f14fd3d6d174 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:12:30 +0330 Subject: [PATCH] Import source: backend/src/openapi.yaml Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- backend/src/openapi.yaml | 523 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) create mode 100644 backend/src/openapi.yaml diff --git a/backend/src/openapi.yaml b/backend/src/openapi.yaml new file mode 100644 index 0000000..8df0962 --- /dev/null +++ b/backend/src/openapi.yaml @@ -0,0 +1,523 @@ +openapi: 3.1.0 +info: + title: CShare API + version: 0.1.0 + description: | + File-sharing API. Ownership is tracked via an HTTP-only session cookie (`cshare_sid`) + issued automatically on first request — no login required for basic uploads/downloads. + +servers: + - url: /api + description: Same-origin (production) + - url: http://localhost:4001/api + description: Local development + +tags: + - name: files + description: Upload, list, and delete files + - name: links + description: Resolve share links and trigger downloads / previews + - name: dashboard + description: Per-session analytics + - name: auth + description: Account registration, login, and token refresh + - name: admin + description: Internal admin endpoints (API-key protected) + +# ── Common response schemas ───────────────────────────────────────────────── + +components: + securitySchemes: + adminKey: + type: apiKey + in: header + name: x-admin-key + + schemas: + Error: + type: object + required: [error, code] + properties: + error: + type: string + code: + type: string + + FileRecord: + type: object + required: [id, linkId, originalName, mimeType, sizeBytes, status, uploader, createdAt, expiresAt, views, downloads, shareUrl] + properties: + id: + type: string + format: uuid + linkId: + type: string + example: "aB3xZ" + originalName: + type: string + example: "report.pdf" + mimeType: + type: string + example: "application/pdf" + sizeBytes: + type: integer + example: 2048000 + status: + type: string + enum: [uploaded, deleted, expired] + uploader: + type: string + example: "You" + createdAt: + type: string + format: date-time + expiresAt: + type: string + format: date-time + views: + type: integer + downloads: + type: integer + shareUrl: + type: string + format: uri + example: "https://file.asabbagh.com/preview/aB3xZ" + + DashboardStats: + type: object + required: [totalFiles, totalViews, totalDownloads, events] + properties: + totalFiles: + type: integer + totalViews: + type: integer + totalDownloads: + type: integer + events: + type: array + items: + type: object + properties: + linkId: + type: string + type: + type: string + enum: [view, download] + createdAt: + type: string + format: date-time + ip: + type: string + userAgent: + type: string + + User: + type: object + required: [id, email, plan, verified] + properties: + id: + type: string + format: uuid + email: + type: string + format: email + plan: + type: string + enum: [free, pro, team] + verified: + type: boolean + +# ── Paths ──────────────────────────────────────────────────────────────────── + +paths: + + /v1/files/upload: + post: + tags: [files] + summary: Upload a file + description: | + Streams the file directly to Google Drive — no disk writes on the server. + Rate limited to **20 uploads per IP per hour**. + Files blocked by MIME type: EXE, DLL, COM, ELF, MSI. + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + expiresIn: + type: integer + description: Expiry in seconds (3 600 – 7 776 000, default 2 592 000) + default: 2592000 + uploader: + type: string + description: Display name shown to recipients (max 80 chars) + default: "You" + responses: + "201": + description: File uploaded + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/FileRecord" + "400": + description: Missing file or not multipart + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "413": + description: File exceeds size limit (500 MB) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "415": + description: Blocked file type (executable) + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "429": + description: Rate limit exceeded + headers: + Retry-After: + schema: + type: integer + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/files: + get: + tags: [files] + summary: List your files + description: Returns only files uploaded in the current browser session (scoped by `cshare_sid` cookie). + parameters: + - name: page + in: query + schema: + type: integer + default: 1 + - name: limit + in: query + schema: + type: integer + default: 100 + maximum: 100 + responses: + "200": + description: Paginated file list + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/FileRecord" + meta: + type: object + properties: + page: + type: integer + limit: + type: integer + total: + type: integer + + /v1/files/{id}: + get: + tags: [files] + summary: Get a single file record + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: File record + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/FileRecord" + "404": + description: File not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + delete: + tags: [files] + summary: Delete a file + description: Only the session that uploaded the file can delete it. + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "204": + description: Deleted + "404": + description: File not found or not owned by this session + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/links/{linkId}: + get: + tags: [links] + summary: Resolve a share link + description: Returns file metadata for the share-link page. Increments view counter. + parameters: + - name: linkId + in: path + required: true + schema: + type: string + example: "aB3xZ" + responses: + "200": + description: File metadata + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/FileRecord" + "404": + description: Link not found or expired + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + + /v1/links/{linkId}/download: + get: + tags: [links] + summary: Download a file + description: Increments download counter then issues a 302 redirect to the Google Drive download URL. + parameters: + - name: linkId + in: path + required: true + schema: + type: string + responses: + "302": + description: Redirect to Google Drive download + headers: + Location: + schema: + type: string + format: uri + "404": + description: Link not found or expired + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "429": + description: Rate limit exceeded + + /v1/links/{linkId}/preview: + get: + tags: [links] + summary: Inline preview stream + description: | + Streams file content for in-browser preview (images, video, audio, text, PDF). + Only available for files ≤ 25 MB with a supported MIME type. + Content-Security-Policy is set to restrict the preview context. + parameters: + - name: linkId + in: path + required: true + schema: + type: string + responses: + "200": + description: File content stream + headers: + Content-Type: + schema: + type: string + Content-Disposition: + schema: + type: string + "404": + description: Link not found or expired + "413": + description: Preview not available (file too large or unsupported type) + + /v1/dashboard/stats: + get: + tags: [dashboard] + summary: Session-scoped analytics + description: Returns aggregated stats (total files, views, downloads) and recent events for the current session only. + responses: + "200": + description: Dashboard stats + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/DashboardStats" + + /auth/register: + post: + tags: [auth] + summary: Create an account + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email, password] + properties: + email: + type: string + format: email + password: + type: string + minLength: 8 + responses: + "201": + description: Account created — returns access token + sets refresh cookie + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + user: + $ref: "#/components/schemas/User" + accessToken: + type: string + "400": + description: Validation error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "429": + description: Auth rate limit (5 per 15 min per IP) + + /auth/login: + post: + tags: [auth] + summary: Log in + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [email, password] + properties: + email: + type: string + format: email + password: + type: string + responses: + "200": + description: Logged in — access token + refresh cookie + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + user: + $ref: "#/components/schemas/User" + accessToken: + type: string + "401": + description: Invalid credentials + "429": + description: Auth rate limit + + /auth/refresh: + post: + tags: [auth] + summary: Refresh access token + description: Reads the `cshare_refresh` HTTP-only cookie. Returns a new access token and rotates the refresh token. + responses: + "200": + description: New access token issued + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + accessToken: + type: string + "401": + description: Missing or expired refresh token + + /auth/logout: + post: + tags: [auth] + summary: Log out + description: Revokes the current refresh token and clears the cookie. + responses: + "204": + description: Logged out + + /admin/overview: + get: + tags: [admin] + summary: Admin overview + security: + - adminKey: [] + responses: + "200": + description: File and user statistics + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + filesByStatus: + type: object + additionalProperties: + type: integer + users: + type: integer + totalEvents: + type: integer + "401": + description: Missing or invalid admin key