A lightweight CDN server with a simple frontend & API.
https://cdn.ziemlich-schnell.de
docker run --name qcdn -p 3000:3000 \
-e NODE_ENV=production \
-e SECURITY=enabled \
-e API_KEYS=none \
-e PASSWORDS=CHANGEME \
-e HEAD_TAGS='<link rel="stylesheet" href="https://cdn.example.com/styles.css">,<script src="https://cdn.example.com/script.js"></script>' \
-e SESSION_SECRET=your-secret-key \
-e USE_HTTPS=true \
-e DOMAIN=localhost \
-e PORT=3000 \
-v ./data:/usr/src/app/data --restart unless-stopped \
ghcr.io/arthur2500/qcdn:latest
or
mkdir qCDN &&
cd qCDN &&
wget https://raw.githubusercontent.com/Arthur2500/qCDN/main/docker-compose.yml &&
docker-compose up -d
git clone https://github.com/Arthur2500/qCDN.git &&
docker-compose -f docker-compose.local.yml up -d --build
Node.js >= 16
git clone https://github.com/Arthur2500/qCDN.git
npm install
SECURITY=enabled \
PASSWORDS=your-passwords-here \
API_KEYS=your-api-key-here \
HEAD_TAGS='<link rel="stylesheet" href="https://cdn.example.com/styles.css">,<script src="https://cdn.example.com/script.js"></script>' \
PRIVACY_LINK=https://example.com/privacy \
TERMS_LINK=https://example.com/terms \
IMPRINT_LINK=https://example.com/imprint \
SESSION_SECRET=your-secret-key \
USE_HTTPS=true \
DOMAIN=your-domain-here \
PORT=3000 \
node server.js
docker-compose.yml Environment Settings:
SECURITY: [enabled/disabled]: Enables/disables security features such as rate limiting and Helmet protectionPASSWORDS: [$PASSWORDS]: Comma-separated passwords to secure frontendAPI_KEYS: [none/$CUSTOM_KEYS]: Comma-separated API tokens (if set to "none", API is disabled)HEAD_TAGS: HTML elements to be included in<head>PRIVACY_LINK: Privacy policy linkTERMS_LINK: Terms of service linkIMPRINT_LINK: Imprint linkSESSION_SECRET: Secret key for session managementUSE_HTTPS: Iftrue, uses HTTPS protocolDOMAIN: Server domain namePORT: Port if run locally
Allows authenticated users to upload files via API and receive a URL for accessing them.
x-api-token: API authentication token.
file: File to upload (multipart/form-data).
- Success:
{
"success": true,
"url": "https://example.com/<hash>/<originalName>"
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X POST http://localhost:3000/api/upload \
-H "x-api-token: YOUR_API_TOKEN" \
-F "file=@/path/to/your/file.txt"import requests
url = "http://localhost:3000/api/upload"
headers = {"x-api-token": "YOUR_API_TOKEN"}
files = {"file": open("/path/to/your/file.txt", "rb")}
response = requests.post(url, headers=headers, files=files)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("File uploaded successfully:", data["url"])
else:
print("Error:", data)
else:
print("Error:", response.text)Uploads a file to a reverse share by its hash. No authentication required.
file: File to upload (multipart/form-data).
- Success:
{
"success": true,
"fileUrl": "<file_url>",
"filename": "<filename>",
"uploadedAt": "<timestamp>"
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X POST http://localhost:3000/api/upload/<hash> \
-F "file=@/path/to/your/file.txt"import requests
url = "http://localhost:3000/api/upload/<hash>"
files = {"file": open("/path/to/your/file.txt", "rb")}
response = requests.post(url, files=files)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("File uploaded successfully:", data["fileUrl"])
else:
print("Error:", data)
else:
print("Error:", response.text)Retrieves information about a reverse share by its hash.
x-api-token: API authentication token.
- Success:
{
"success": true,
"used": true | false,
"filename": "<filename>",
"fileUrl": "<file_url>",
"uploadedAt": "<timestamp>",
"createdAt": "<timestamp>",
"callbackUrl": "<callback_url>"
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X GET http://localhost:3000/api/<hash> \
-H "x-api-token: YOUR_API_TOKEN"import requests
url = "http://localhost:3000/api/<hash>"
headers = {"x-api-token": "YOUR_API_TOKEN"}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("Reverse share details:", data)
else:
print("Error:", data)
else:
print("Error:", response.text)Allows authenticated users to delete an uploaded file or reverse share by hash.
x-api-token: API authentication token.
- Success:
{
"success": true,
"type": "file" | "reverse-share"
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X DELETE http://localhost:3000/api/<hash> \
-H "x-api-token: YOUR_API_TOKEN"import requests
url = "http://localhost:3000/api/<hash>"
headers = {"x-api-token": "YOUR_API_TOKEN"}
response = requests.delete(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("Deleted successfully")
else:
print("Error:", data)
else:
print("Error:", response.text)Creates a new reverse share.
x-api-token: API authentication token.
callbackUrl(optional): URL to be called after the file is uploaded.
- Success:
{
"success": true,
"url": "<reverse_share_url>"
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X POST http://localhost:3000/api/reverse-share \
-H "x-api-token: YOUR_API_TOKEN" \
-d '{"callbackUrl": "https://example.com/callback"}'import requests
url = "http://localhost:3000/api/reverse-share"
headers = {"x-api-token": "YOUR_API_TOKEN"}
data = {"callbackUrl": "https://example.com/callback"}
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("Reverse share created successfully:", data["url"])
else:
print("Error:", data)
else:
print("Error:", response.text)Lists all uploaded files.
x-api-token: API authentication token.
- Success:
{
"success": true,
"uploads": [<file_objects>]
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X GET http://localhost:3000/api/list \
-H "x-api-token: YOUR_API_TOKEN"import requests
url = "http://localhost:3000/api/list"
headers = {"x-api-token": "YOUR_API_TOKEN"}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("Uploads:", data["uploads"])
else:
print("Error:", data)
else:
print("Error:", response.text)Lists all reverse shares.
x-api-token: API authentication token.
- Success:
{
"success": true,
"reverseShares": [<reverse_share_objects>]
}- Failure:
{
"success": false,
"message": "Error message"
}curl -X GET http://localhost:3000/api/list/reverse-shares \
-H "x-api-token: YOUR_API_TOKEN"import requests
url = "http://localhost:3000/api/list/reverse-shares"
headers = {"x-api-token": "YOUR_API_TOKEN"}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get("success"):
print("Reverse Shares:", data["reverseShares"])
else:
print("Error:", data)
else:
print("Error:", response.text)
