mirror of
https://github.com/DumbWareio/DumbDrop.git
synced 2025-10-23 07:41:58 +00:00
Add customizable site title feature via DUMBDROP_TITLE environment variable
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
# Server Configuration
|
# Server Configuration
|
||||||
PORT=3000 # The port the server will listen on
|
PORT=3000 # The port the server will listen on
|
||||||
|
DUMBDROP_TITLE=DumbDrop # Site title displayed in header (default: DumbDrop)
|
||||||
|
|
||||||
# Upload Limits
|
# Upload Limits
|
||||||
MAX_FILE_SIZE=1024 # Maximum file size in MB (default: 1024 MB / 1 GB)
|
MAX_FILE_SIZE=1024 # Maximum file size in MB (default: 1024 MB / 1 GB)
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
DUMBDROP_PIN= # Optional 4-digit PIN protection (leave empty to disable)
|
DUMBDROP_PIN= # Optional PIN protection (4-10 digits, leave empty to disable)
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
APPRISE_URL= # Apprise URL for notifications (leave empty to disable)
|
APPRISE_URL= # Apprise URL for notifications (leave empty to disable)
|
||||||
|
@@ -28,8 +28,9 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
|
|||||||
| PORT | Server port | 3000 | No |
|
| PORT | Server port | 3000 | No |
|
||||||
| MAX_FILE_SIZE| Maximum file size in MB | 1024 | No |
|
| MAX_FILE_SIZE| Maximum file size in MB | 1024 | No |
|
||||||
| DUMBDROP_PIN | PIN protection (4-10 digits) | None | No |
|
| DUMBDROP_PIN | PIN protection (4-10 digits) | None | No |
|
||||||
|
| DUMBDROP_TITLE| Site title displayed in header | DumbDrop| No |
|
||||||
| APPRISE_URL | Apprise URL for notifications | None | No |
|
| APPRISE_URL | Apprise URL for notifications | None | No |
|
||||||
| APPRISE_MESSAGE| Notification message template | "File uploaded: {filename}" | No |
|
| APPRISE_MESSAGE| Notification message template | "File uploaded: {filename}" | No |
|
||||||
|
|
||||||
## Security Features
|
## Security Features
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>DumbDrop - Simple File Upload</title>
|
<title>{{SITE_TITLE}} - Simple File Upload</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<line class="sun" x1="18.36" y1="5.64" x2="19.78" y2="4.22" style="display:none"/>
|
<line class="sun" x1="18.36" y1="5.64" x2="19.78" y2="4.22" style="display:none"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<h1>DumbDrop</h1>
|
<h1>{{SITE_TITLE}}</h1>
|
||||||
<div class="upload-container" id="dropZone">
|
<div class="upload-container" id="dropZone">
|
||||||
<div class="upload-content">
|
<div class="upload-content">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>DumbDrop - Login</title>
|
<title>{{SITE_TITLE}} - Login</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
<style>
|
<style>
|
||||||
.login-container {
|
.login-container {
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="pin-header">
|
<div class="pin-header">
|
||||||
<h1>DumbDrop</h1>
|
<h1>{{SITE_TITLE}}</h1>
|
||||||
<h2>Enter PIN</h2>
|
<h2>Enter PIN</h2>
|
||||||
</div>
|
</div>
|
||||||
<form id="pin-form">
|
<form id="pin-form">
|
||||||
|
58
server.js
58
server.js
@@ -16,6 +16,7 @@ const uploadDir = './uploads'; // Local development
|
|||||||
const maxFileSize = parseInt(process.env.MAX_FILE_SIZE || '1024') * 1024 * 1024; // Convert MB to bytes
|
const maxFileSize = parseInt(process.env.MAX_FILE_SIZE || '1024') * 1024 * 1024; // Convert MB to bytes
|
||||||
const APPRISE_URL = process.env.APPRISE_URL;
|
const APPRISE_URL = process.env.APPRISE_URL;
|
||||||
const APPRISE_MESSAGE = process.env.APPRISE_MESSAGE || 'File uploaded: {filename}';
|
const APPRISE_MESSAGE = process.env.APPRISE_MESSAGE || 'File uploaded: {filename}';
|
||||||
|
const siteTitle = process.env.DUMBDROP_TITLE || 'DumbDrop';
|
||||||
|
|
||||||
// Brute force protection setup
|
// Brute force protection setup
|
||||||
const loginAttempts = new Map(); // Stores IP addresses and their attempt counts
|
const loginAttempts = new Map(); // Stores IP addresses and their attempt counts
|
||||||
@@ -191,43 +192,29 @@ const requirePin = (req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply pin protection to all /upload routes
|
// Move the root and login routes before static file serving
|
||||||
app.use('/upload', requirePin);
|
|
||||||
|
|
||||||
// Serve login page and its assets without PIN check
|
|
||||||
app.use((req, res, next) => {
|
|
||||||
if (req.path === '/login.html' || req.path === '/styles.css' || req.path.startsWith('/api/')) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check PIN requirement
|
|
||||||
if (!PIN) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cookie
|
|
||||||
const providedPin = req.cookies.DUMBDROP_PIN;
|
|
||||||
if (!safeCompare(providedPin, PIN)) {
|
|
||||||
// If requesting HTML or root, redirect to login
|
|
||||||
if (req.path === '/' || req.path.endsWith('.html')) {
|
|
||||||
return res.redirect('/login.html');
|
|
||||||
}
|
|
||||||
return res.status(401).json({ error: 'Unauthorized' });
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Serve static files
|
|
||||||
app.use(express.static('public'));
|
|
||||||
|
|
||||||
// Handle root route
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
if (PIN && !safeCompare(req.cookies.DUMBDROP_PIN, PIN)) {
|
if (PIN && !safeCompare(req.cookies.DUMBDROP_PIN, PIN)) {
|
||||||
return res.redirect('/login.html');
|
return res.redirect('/login.html');
|
||||||
}
|
}
|
||||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
// Read the file and replace the title
|
||||||
|
let html = fs.readFileSync(path.join(__dirname, 'public', 'index.html'), 'utf8');
|
||||||
|
html = html.replace(/{{SITE_TITLE}}/g, siteTitle); // Use global replace
|
||||||
|
res.send(html);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/login.html', (req, res) => {
|
||||||
|
let html = fs.readFileSync(path.join(__dirname, 'public', 'login.html'), 'utf8');
|
||||||
|
html = html.replace(/{{SITE_TITLE}}/g, siteTitle); // Use global replace
|
||||||
|
res.send(html);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move static file serving after our dynamic routes
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// PIN protection middleware should be before the routes that need protection
|
||||||
|
app.use('/upload', requirePin);
|
||||||
|
|
||||||
// Store ongoing uploads
|
// Store ongoing uploads
|
||||||
const uploads = new Map();
|
const uploads = new Map();
|
||||||
|
|
||||||
@@ -335,11 +322,14 @@ app.listen(port, () => {
|
|||||||
log.info(`Server running at http://localhost:${port}`);
|
log.info(`Server running at http://localhost:${port}`);
|
||||||
log.info(`Upload directory: ${uploadDir}`);
|
log.info(`Upload directory: ${uploadDir}`);
|
||||||
|
|
||||||
|
// Log custom title if set
|
||||||
|
if (process.env.DUMBDROP_TITLE) {
|
||||||
|
log.info(`Custom title set to: ${siteTitle}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Add Apprise configuration logging
|
// Add Apprise configuration logging
|
||||||
if (APPRISE_URL) {
|
if (APPRISE_URL) {
|
||||||
log.info(`Apprise notifications enabled`);
|
log.info('Apprise notifications enabled');
|
||||||
log.info(`Apprise URL: ${APPRISE_URL}`);
|
|
||||||
log.info(`Apprise message template: ${APPRISE_MESSAGE}`);
|
|
||||||
} else {
|
} else {
|
||||||
log.info('Apprise notifications disabled - no URL configured');
|
log.info('Apprise notifications disabled - no URL configured');
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user