mirror of
https://github.com/DumbWareio/DumbDrop.git
synced 2025-10-23 07:41:58 +00:00
Merge pull request #12 from greirson/add-notifications
Add notifications support via Apprise
This commit is contained in:
@@ -5,4 +5,8 @@ PORT=3000 # The port the server will listen on
|
|||||||
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 4-digit PIN protection (leave empty to disable)
|
||||||
|
|
||||||
|
# Notifications
|
||||||
|
APPRISE_URL= # Apprise URL for notifications (leave empty to disable)
|
||||||
|
APPRISE_MESSAGE= # Custom message for notifications (default: "File uploaded: {filename}")
|
11
Dockerfile
11
Dockerfile
@@ -1,5 +1,16 @@
|
|||||||
FROM node:18-alpine
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
# Install python and create virtual environment
|
||||||
|
RUN apk add --no-cache python3 py3-pip && \
|
||||||
|
python3 -m venv /opt/venv
|
||||||
|
|
||||||
|
# Activate virtual environment and install apprise
|
||||||
|
RUN . /opt/venv/bin/activate && \
|
||||||
|
pip install --no-cache-dir apprise
|
||||||
|
|
||||||
|
# Add virtual environment to PATH
|
||||||
|
ENV PATH="/opt/venv/bin:$PATH"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
12
README.md
12
README.md
@@ -18,6 +18,8 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
|
|||||||
- Configurable file size limits
|
- Configurable file size limits
|
||||||
- Drag and Drop Directory Support (Maintains file structure in upload)
|
- Drag and Drop Directory Support (Maintains file structure in upload)
|
||||||
- Optional PIN protection (4-10 digits) with secure validation
|
- Optional PIN protection (4-10 digits) with secure validation
|
||||||
|
- Configurable notifications via Apprise
|
||||||
|
- Custom notification messages with filename templating
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
@@ -26,6 +28,8 @@ 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 |
|
||||||
|
| APPRISE_URL | Apprise URL for notifications | None | No |
|
||||||
|
| APPRISE_MESSAGE| Notification message template | "File uploaded: {filename}" | No |
|
||||||
|
|
||||||
## Security Features
|
## Security Features
|
||||||
|
|
||||||
@@ -35,6 +39,12 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
|
|||||||
- Secure PIN validation middleware
|
- Secure PIN validation middleware
|
||||||
- No PIN storage in browser (memory only)
|
- No PIN storage in browser (memory only)
|
||||||
|
|
||||||
|
## Notification Support
|
||||||
|
- Integration with [Apprise](https://github.com/caronc/apprise?tab=readme-ov-file#supported-notifications) for flexible notifications
|
||||||
|
- Support for all Apprise notification services
|
||||||
|
- Customizable notification messages with filename templating
|
||||||
|
- Optional - disabled if no APPRISE_URL is set
|
||||||
|
|
||||||
# Future Features
|
# Future Features
|
||||||
- Camera Upload for Mobile
|
- Camera Upload for Mobile
|
||||||
- Enhanced Progress Features (upload speed display, time remaining estimation)
|
- Enhanced Progress Features (upload speed display, time remaining estimation)
|
||||||
@@ -87,6 +97,8 @@ services:
|
|||||||
- $(pwd)/local_uploads:/app/uploads
|
- $(pwd)/local_uploads:/app/uploads
|
||||||
environment:
|
environment:
|
||||||
- DUMBDROP_PIN=123456
|
- DUMBDROP_PIN=123456
|
||||||
|
# - APPRISE_URL= # i.e. tgram://bottoken/ChatID
|
||||||
|
# - APPRISE_MESSAGE=
|
||||||
image: abite3/dumbdrop:latest
|
image: abite3/dumbdrop:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -4,7 +4,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 3000:3000
|
- 3000:3000
|
||||||
volumes:
|
volumes:
|
||||||
- $(pwd)/local_uploads:/app/uploads
|
- ./local_uploads:/app/uploads
|
||||||
environment:
|
environment:
|
||||||
- DUMBDROP_PIN=123456
|
- DUMBDROP_PIN=123456
|
||||||
|
- APPRISE_URL= # i.e. tgram://bottoken/ChatID
|
||||||
|
- APPRISE_MESSAGE= # dont add quotes here
|
||||||
image: abite3/dumbdrop:latest
|
image: abite3/dumbdrop:latest
|
@@ -12,6 +12,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "A simple file upload application",
|
"description": "A simple file upload application",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"apprise": "^1.0.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
32
server.js
32
server.js
@@ -5,12 +5,17 @@ const cors = require('cors');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require('cookie-parser');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const util = require('util');
|
||||||
|
const execAsync = util.promisify(exec);
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = process.env.PORT || 3000;
|
const port = process.env.PORT || 3000;
|
||||||
const uploadDir = './uploads'; // Local development
|
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_MESSAGE = process.env.APPRISE_MESSAGE || 'File uploaded: {filename}';
|
||||||
|
|
||||||
// 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
|
||||||
@@ -291,6 +296,9 @@ app.post('/upload/chunk/:uploadId', express.raw({
|
|||||||
upload.writeStream.end();
|
upload.writeStream.end();
|
||||||
uploads.delete(uploadId);
|
uploads.delete(uploadId);
|
||||||
log.success(`Upload completed: ${upload.filename}`);
|
log.success(`Upload completed: ${upload.filename}`);
|
||||||
|
|
||||||
|
// Add notification here
|
||||||
|
sendNotification(upload.filename);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(`Chunk upload failed: ${err.message}`);
|
log.error(`Chunk upload failed: ${err.message}`);
|
||||||
@@ -325,6 +333,15 @@ 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}`);
|
||||||
|
|
||||||
|
// Add Apprise configuration logging
|
||||||
|
if (APPRISE_URL) {
|
||||||
|
log.info(`Apprise notifications enabled`);
|
||||||
|
log.info(`Apprise URL: ${APPRISE_URL}`);
|
||||||
|
log.info(`Apprise message template: ${APPRISE_MESSAGE}`);
|
||||||
|
} else {
|
||||||
|
log.info('Apprise notifications disabled - no URL configured');
|
||||||
|
}
|
||||||
|
|
||||||
// List directory contents
|
// List directory contents
|
||||||
try {
|
try {
|
||||||
const files = fs.readdirSync(uploadDir);
|
const files = fs.readdirSync(uploadDir);
|
||||||
@@ -336,3 +353,18 @@ app.listen(port, () => {
|
|||||||
log.error(`Failed to list directory contents: ${err.message}`);
|
log.error(`Failed to list directory contents: ${err.message}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add this helper function after other helper functions
|
||||||
|
async function sendNotification(filename) {
|
||||||
|
if (!APPRISE_URL) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const message = APPRISE_MESSAGE.replace('{filename}', filename);
|
||||||
|
|
||||||
|
// Execute apprise command
|
||||||
|
await execAsync(`apprise "${APPRISE_URL}" -b "${message}"`);
|
||||||
|
log.info(`Notification sent for: ${filename}`);
|
||||||
|
} catch (err) {
|
||||||
|
log.error(`Failed to send notification: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user