Add public download endpoint without authentication
Features: - New GET /public/download/:filename endpoint for public file access - No authentication required for downloading processed files - Directory traversal protection with path.basename() - Proper Content-Disposition headers for browser downloads Documentation: - Updated docs/API.md with public endpoint section - Added to table of contents - Usage examples and security notes Use cases: - Share download links via email/chat/social media - Embed in web applications without auth - Direct browser downloads - Public file sharing Security: - Path sanitization prevents directory traversal attacks - Only files in OUTPUT_DIR are accessible - Returns proper 404 for non-existent files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
751a382ccd
commit
eb69e8b063
66
docs/API.md
66
docs/API.md
@ -68,6 +68,7 @@ curl -H "Authorization: Bearer your_api_token_here" http://localhost:8888/endpoi
|
||||
## Table of Contents
|
||||
- [Authentication](#-authentication)
|
||||
- [Health & Info](#health--info)
|
||||
- [Public Download Endpoint](#public-download-endpoint)
|
||||
- [Download Endpoints](#download-endpoints)
|
||||
- [Transcription Endpoints](#transcription-endpoints)
|
||||
- [Conversion Endpoints](#conversion-endpoints)
|
||||
@ -84,6 +85,8 @@ curl -H "Authorization: Bearer your_api_token_here" http://localhost:8888/endpoi
|
||||
### GET /health
|
||||
Health check endpoint.
|
||||
|
||||
**Authentication**: Not required (public)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
@ -95,6 +98,8 @@ Health check endpoint.
|
||||
### GET /api
|
||||
Get API information and available endpoints.
|
||||
|
||||
**Authentication**: Not required (public)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
@ -104,6 +109,67 @@ Get API information and available endpoints.
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Public Download Endpoint
|
||||
|
||||
### GET /public/download/:filename
|
||||
Public endpoint to download files without authentication.
|
||||
|
||||
**Authentication**: Not required (public)
|
||||
|
||||
**Purpose**: Share direct download links for generated files (MP3, transcriptions, translations, summaries) without requiring API authentication.
|
||||
|
||||
**URL Parameters:**
|
||||
- `filename` (required): Name of the file to download
|
||||
|
||||
**Security**:
|
||||
- Directory traversal protection enabled (uses `path.basename()`)
|
||||
- Only files in the configured OUTPUT_DIR are accessible
|
||||
- No authentication required
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
# Direct download (no auth needed)
|
||||
curl -O http://localhost:8888/public/download/my_video.mp3
|
||||
|
||||
# Or simply open in browser
|
||||
http://localhost:8888/public/download/my_video.mp3
|
||||
```
|
||||
|
||||
**Response (Success):**
|
||||
- File download with proper Content-Disposition headers
|
||||
- Browser will prompt to download the file
|
||||
|
||||
**Response (Error - 404):**
|
||||
```json
|
||||
{
|
||||
"error": "File not found",
|
||||
"message": "File 'my_video.mp3' does not exist"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Error - 500):**
|
||||
```json
|
||||
{
|
||||
"error": "Download failed",
|
||||
"message": "Error details..."
|
||||
}
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Share download links via email/chat
|
||||
- Embed in web applications
|
||||
- Direct browser downloads
|
||||
- Public file sharing
|
||||
|
||||
**Note**: After processing (download, transcription, etc.), use the returned `filePath` or `fileUrl` from authenticated endpoints, then construct public URL:
|
||||
```
|
||||
/public/download/{basename_of_filePath}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /info
|
||||
Get information about a YouTube video or playlist.
|
||||
|
||||
|
||||
14
refresh-cookies.sh
Executable file
14
refresh-cookies.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# Auto-refresh cookies from /tmp/share if they exist and are newer
|
||||
|
||||
SHARE_COOKIES="/tmp/share/youtube-cookies.txt"
|
||||
LOCAL_COOKIES="/home/debian/videotomp3transcriptor/youtube-cookies.txt"
|
||||
|
||||
if [ -f "$SHARE_COOKIES" ]; then
|
||||
# Copy if share is newer or local doesn't exist
|
||||
if [ ! -f "$LOCAL_COOKIES" ] || [ "$SHARE_COOKIES" -nt "$LOCAL_COOKIES" ]; then
|
||||
cp "$SHARE_COOKIES" "$LOCAL_COOKIES"
|
||||
chmod 600 "$LOCAL_COOKIES"
|
||||
echo "[$(date)] ✓ Cookies refreshed from /tmp/share"
|
||||
fi
|
||||
fi
|
||||
@ -102,7 +102,8 @@ app.use((req, res, next) => {
|
||||
const authenticate = (req, res, next) => {
|
||||
// Skip authentication for public endpoints
|
||||
const publicEndpoints = ['/health', '/api'];
|
||||
if (publicEndpoints.includes(req.path)) {
|
||||
// Allow public download endpoint
|
||||
if (publicEndpoints.includes(req.path) || req.path.startsWith('/public/download/')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -173,6 +174,47 @@ app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /public/download/:filename
|
||||
* Public endpoint to download files without authentication
|
||||
*/
|
||||
app.get('/public/download/:filename', (req, res) => {
|
||||
try {
|
||||
const { filename } = req.params;
|
||||
|
||||
// Security: prevent directory traversal
|
||||
const safeName = path.basename(filename);
|
||||
const filePath = path.join(OUTPUT_DIR, safeName);
|
||||
|
||||
// Check if file exists
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return res.status(404).json({
|
||||
error: 'File not found',
|
||||
message: `File '${safeName}' does not exist`
|
||||
});
|
||||
}
|
||||
|
||||
// Send file with proper headers
|
||||
res.download(filePath, safeName, (err) => {
|
||||
if (err) {
|
||||
console.error(`[Public Download] Error: ${err.message}`);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
error: 'Download failed',
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`[Public Download] Error: ${error.message}`);
|
||||
res.status(500).json({
|
||||
error: 'Server error',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /info?url=<youtube_url>
|
||||
* Get info about a video or playlist
|
||||
@ -1429,6 +1471,7 @@ app.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
console.log('\nEndpoints:');
|
||||
console.log(' GET /health - Health check');
|
||||
console.log(' GET /public/download/:filename - Public file download (no auth)');
|
||||
console.log(' GET /info?url= - Get video/playlist info');
|
||||
console.log(' POST /download - Download as MP3');
|
||||
console.log(' POST /transcribe - Transcribe audio file');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user