feat: enhance Apprise notifications with file size support

- Add file size formatting to notifications with auto-scaling units (B, KB, MB, GB, TB)
- Add APPRISE_SIZE_UNIT environment variable for fixed size units
- Update default notification message to include file size: "New file uploaded: {filename} ({size})"
- Fix filename reference in notifications to use safeFilename
- Fix async/await handling in upload chunk handler
- Add size formatting documentation to README
- Update environment variable documentation

Example notification: "New file uploaded: example.pdf (2.54MB)"
This commit is contained in:
Greirson Lee-Thorp
2025-01-31 21:07:53 -08:00
parent 2b78c12009
commit b91f82f3aa
3 changed files with 65 additions and 20 deletions

View File

@@ -10,4 +10,5 @@ DUMBDROP_PIN= # Optional PIN protection (4-10 digits, leave empty to
# Notifications
APPRISE_URL= # Apprise URL for notifications (leave empty to disable)
APPRISE_MESSAGE= # Custom message for notifications (default: "File uploaded: {filename}")
APPRISE_MESSAGE= # Custom message for notifications (default: "New file uploaded: {filename} ({size})")
APPRISE_SIZE_UNIT= # Size unit for notifications (B, KB, MB, GB, TB). Leave empty for auto

View File

@@ -23,14 +23,29 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
## Environment Variables
| Variable | Description | Default | Required |
|--------------|---------------------------------------|---------|----------|
| PORT | Server port | 3000 | No |
| MAX_FILE_SIZE| Maximum file size in MB | 1024 | 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_MESSAGE| Notification message template | "File uploaded: {filename}" | No |
| Variable | Description | Default | Required |
|------------------|---------------------------------------|---------|----------|
| PORT | Server port | 3000 | No |
| MAX_FILE_SIZE | Maximum file size in MB | 1024 | 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_MESSAGE | Notification message template | "New file uploaded: {filename} ({size})" | No |
| APPRISE_SIZE_UNIT| Size unit for notifications | Auto | No |
## Notification Templates
The notification message supports the following placeholders:
- `{filename}`: Name of the uploaded file
- `{size}`: Size of the file (formatted according to APPRISE_SIZE_UNIT)
Example message template:
```env
APPRISE_MESSAGE="New file uploaded: {filename} ({size})"
```
Size formatting examples:
- Auto (default): Chooses nearest unit (e.g., "1.44MB", "256KB")
- Fixed unit: Set APPRISE_SIZE_UNIT to B, KB, MB, GB, or TB
## Security Features

View File

@@ -15,8 +15,9 @@ const port = process.env.PORT || 3000;
const uploadDir = './uploads'; // Local development
const maxFileSize = parseInt(process.env.MAX_FILE_SIZE || '1024') * 1024 * 1024; // Convert MB to bytes
const APPRISE_URL = process.env.APPRISE_URL;
const APPRISE_MESSAGE = process.env.APPRISE_MESSAGE || 'File uploaded: {filename}';
const APPRISE_MESSAGE = process.env.APPRISE_MESSAGE || 'New file uploaded: {filename} ({size})';
const siteTitle = process.env.DUMBDROP_TITLE || 'DumbDrop';
const APPRISE_SIZE_UNIT = process.env.APPRISE_SIZE_UNIT;
// Brute force protection setup
const loginAttempts = new Map(); // Stores IP addresses and their attempt counts
@@ -259,7 +260,7 @@ app.post('/upload/init', async (req, res) => {
app.post('/upload/chunk/:uploadId', express.raw({
limit: '10mb',
type: 'application/octet-stream'
}), (req, res) => {
}), async (req, res) => {
const { uploadId } = req.params;
const upload = uploads.get(uploadId);
const chunkSize = req.body.length;
@@ -273,7 +274,7 @@ app.post('/upload/chunk/:uploadId', express.raw({
upload.bytesReceived += chunkSize;
const progress = Math.round((upload.bytesReceived / upload.fileSize) * 100);
log.info(`Received chunk for ${upload.filename}: ${progress}%`);
log.info(`Received chunk for ${upload.safeFilename}: ${progress}%`);
res.json({
bytesReceived: upload.bytesReceived,
@@ -284,10 +285,10 @@ app.post('/upload/chunk/:uploadId', express.raw({
if (upload.bytesReceived >= upload.fileSize) {
upload.writeStream.end();
uploads.delete(uploadId);
log.success(`Upload completed: ${upload.filename}`);
log.success(`Upload completed: ${upload.safeFilename}`);
// Add notification here
sendNotification(upload.filename);
// Update notification call to use safeFilename
await sendNotification(upload.safeFilename, upload.fileSize);
}
} catch (err) {
log.error(`Chunk upload failed: ${err.message}`);
@@ -305,7 +306,7 @@ app.post('/upload/cancel/:uploadId', (req, res) => {
if (err) log.error(`Failed to delete incomplete upload: ${err.message}`);
});
uploads.delete(uploadId);
log.info(`Upload cancelled: ${upload.filename}`);
log.info(`Upload cancelled: ${upload.safeFilename}`);
}
res.json({ message: 'Upload cancelled' });
@@ -346,16 +347,44 @@ app.listen(port, () => {
}
});
// Add this helper function after other helper functions
async function sendNotification(filename) {
// Remove async from formatFileSize function
function formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = bytes;
let unitIndex = 0;
// If a specific unit is requested
if (APPRISE_SIZE_UNIT) {
const requestedUnit = APPRISE_SIZE_UNIT.toUpperCase();
const unitIndex = units.indexOf(requestedUnit);
if (unitIndex !== -1) {
size = bytes / Math.pow(1024, unitIndex);
return size.toFixed(2) + requestedUnit;
}
}
// Auto format to nearest unit
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
// Round to 2 decimal places
return size.toFixed(2) + units[unitIndex];
}
async function sendNotification(filename, fileSize) {
if (!APPRISE_URL) return;
try {
const message = APPRISE_MESSAGE.replace('{filename}', filename);
const formattedSize = formatFileSize(fileSize); // No await needed here
const message = APPRISE_MESSAGE
.replace('{filename}', filename)
.replace('{size}', formattedSize);
// Execute apprise command
await execAsync(`apprise "${APPRISE_URL}" -b "${message}"`);
log.info(`Notification sent for: ${filename}`);
log.info(`Notification sent for: ${filename} (${formattedSize})`);
} catch (err) {
log.error(`Failed to send notification: ${err.message}`);
}