feat: add CORS support, dynamic API_BASE for Netlify frontend, and netlify.toml

This commit is contained in:
katR 2026-09-12 09:55:55 +05:30
parent 9fc9709f69
commit 55015d94d1
4 changed files with 39 additions and 4 deletions

8
netlify.toml Normal file
View file

@ -0,0 +1,8 @@
[build]
publish = "public"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"

10
public/_redirects Normal file
View file

@ -0,0 +1,10 @@
# Netlify redirects file
# To proxy API requests through Netlify (bypassing mixed content & CORS),
# replace YOUR_BACKEND_URL with your Termux backend URL (e.g., Cloudflare Tunnel / ngrok URL):
# /files https://YOUR_BACKEND_URL/files 200
# /upload https://YOUR_BACKEND_URL/upload 200
# /uploads/* https://YOUR_BACKEND_URL/uploads/:splat 200
# /delete/* https://YOUR_BACKEND_URL/delete/:splat 200
# SPA fallback rule
/* /index.html 200

View file

@ -17,6 +17,14 @@
let selectedFiles = [];
// API Base configuration for remote frontend (Netlify) or local hosting
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('api')) {
const customApi = urlParams.get('api').replace(/\/+$/, '');
localStorage.setItem('katdrop_api', customApi);
}
const API_BASE = window.KATDROP_API || localStorage.getItem('katdrop_api') || '';
// Toast
let toastTimeout;
function showToast(msg) {
@ -221,7 +229,7 @@
// Fetch and display files
async function fetchFiles() {
try {
const res = await fetch('/files');
const res = await fetch(`${API_BASE}/files`);
const files = await res.json();
fileList.innerHTML = '';
@ -252,7 +260,7 @@
</div>
</div>
<div class="file-actions">
<a href="/uploads/${encodeURIComponent(name)}" class="action-btn action-download" download>download</a>
<a href="${API_BASE}/uploads/${encodeURIComponent(name)}" class="action-btn action-download" download>download</a>
<button class="action-btn action-delete" onclick="deleteFile('${escapeHtml(name)}', this.closest('li'))">delete</button>
</div>
`;
@ -271,7 +279,7 @@
if (cardElement) cardElement.classList.add('deleting');
try {
const res = await fetch(`/delete/${encodeURIComponent(filename)}`, { method: 'DELETE' });
const res = await fetch(`${API_BASE}/delete/${encodeURIComponent(filename)}`, { method: 'DELETE' });
if (res.ok) {
showToast('file deleted');
setTimeout(() => {
@ -343,7 +351,7 @@
showToast('upload failed');
};
xhr.open('POST', '/upload');
xhr.open('POST', `${API_BASE}/upload`);
xhr.send(formData);
}

View file

@ -92,6 +92,15 @@ const upload = multer({
});
// Middlewares
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
app.use(express.json());
app.use(express.static(publicDir));