Import source: backend/src/openapi.yaml
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -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
|
||||||
Reference in New Issue
Block a user