mirror of
https://github.com/DumbWareio/DumbDrop.git
synced 2025-10-22 23:31:57 +00:00
Compare commits
2 Commits
107684fe6a
...
bf1c9a2dbd
Author | SHA1 | Date | |
---|---|---|---|
|
bf1c9a2dbd | ||
|
e963f2bcde |
78
.env.example
78
.env.example
@@ -1,18 +1,68 @@
|
||||
# Server Configuration
|
||||
PORT=3000 # The port the server will listen on
|
||||
BASE_URL=http://localhost:3000 # The base URL for the application
|
||||
#########################################
|
||||
# SERVER CONFIGURATION
|
||||
#########################################
|
||||
|
||||
# Upload Settings
|
||||
MAX_FILE_SIZE=1024 # Maximum file size in MB
|
||||
AUTO_UPLOAD=false # Enable automatic upload on file selection
|
||||
# Port for the server (default: 3000)
|
||||
PORT=3000
|
||||
|
||||
# Security
|
||||
DUMBDROP_PIN= # Optional PIN protection (4-10 digits)
|
||||
DUMBDROP_TITLE=DumbDrop # Site title displayed in header
|
||||
# Base URL for the application (default: http://localhost:PORT)
|
||||
BASE_URL=http://localhost:3000/
|
||||
|
||||
# Notifications (Optional)
|
||||
APPRISE_URL= # Apprise URL for notifications (e.g., tgram://bottoken/ChatID)
|
||||
APPRISE_MESSAGE=New file uploaded - {filename} ({size}), Storage used {storage}
|
||||
APPRISE_SIZE_UNIT=auto # Size unit for notifications (auto, B, KB, MB, GB, TB)
|
||||
# Node environment (default: development)
|
||||
NODE_ENV=development
|
||||
|
||||
DEMO_MODE=false
|
||||
#########################################
|
||||
# FILE UPLOAD SETTINGS
|
||||
#########################################
|
||||
|
||||
# Maximum file size in MB (default: 1024)
|
||||
MAX_FILE_SIZE=1024
|
||||
|
||||
# Directory for uploads (Docker/production; optional)
|
||||
UPLOAD_DIR=
|
||||
|
||||
# Directory for uploads (local dev, fallback: './local_uploads')
|
||||
LOCAL_UPLOAD_DIR=./local_uploads
|
||||
|
||||
# Comma-separated list of allowed file extensions (optional, e.g. .jpg,.png,.pdf)
|
||||
# ALLOWED_EXTENSIONS=.jpg,.png,.pdf
|
||||
ALLOWED_EXTENSIONS=
|
||||
|
||||
#########################################
|
||||
# SECURITY
|
||||
#########################################
|
||||
|
||||
# PIN protection (4-10 digits, optional)
|
||||
# DUMBDROP_PIN=1234
|
||||
DUMBDROP_PIN=
|
||||
|
||||
#########################################
|
||||
# UI SETTINGS
|
||||
#########################################
|
||||
|
||||
# Site title displayed in header (default: DumbDrop)
|
||||
DUMBDROP_TITLE=DumbDrop
|
||||
|
||||
#########################################
|
||||
# NOTIFICATION SETTINGS
|
||||
#########################################
|
||||
|
||||
# Apprise URL for notifications (optional)
|
||||
APPRISE_URL=
|
||||
|
||||
# Notification message template (default: New file uploaded {filename} ({size}), Storage used {storage})
|
||||
APPRISE_MESSAGE=New file uploaded {filename} ({size}), Storage used {storage}
|
||||
|
||||
# Size unit for notifications (B, KB, MB, GB, TB, or Auto; default: Auto)
|
||||
APPRISE_SIZE_UNIT=Auto
|
||||
|
||||
#########################################
|
||||
# ADVANCED
|
||||
#########################################
|
||||
|
||||
# Enable automatic upload on file selection (true/false, default: false)
|
||||
AUTO_UPLOAD=false
|
||||
|
||||
# Comma-separated list of origins allowed to embed the app in an iframe (optional)
|
||||
# ALLOWED_IFRAME_ORIGINS=https://example.com,https://another.com
|
||||
ALLOWED_IFRAME_ORIGINS=
|
36
.gitignore
vendored
36
.gitignore
vendored
@@ -203,4 +203,38 @@ Thumbs.db
|
||||
*.log
|
||||
.env.*
|
||||
!.env.example
|
||||
!dev/.env.dev.example
|
||||
!dev/.env.dev.example
|
||||
|
||||
# Added by Claude Task Master
|
||||
dev-debug.log
|
||||
# Environment variables
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
# OS specific
|
||||
# Task files
|
||||
.windsurfrules
|
||||
README-task-master.md
|
||||
.cursor/mcp.json
|
||||
.cursor/rules/cursor_rules.mdc
|
||||
.cursor/rules/dev_workflow.mdc
|
||||
.cursor/rules/self_improve.mdc
|
||||
.cursor/rules/taskmaster.mdc
|
||||
scripts/example_prd.txt
|
||||
scripts/prd.txt
|
||||
tasks/task_001.txt
|
||||
tasks/task_002.txt
|
||||
tasks/task_003.txt
|
||||
tasks/task_004.txt
|
||||
tasks/task_005.txt
|
||||
tasks/task_006.txt
|
||||
tasks/task_007.txt
|
||||
tasks/task_008.txt
|
||||
tasks/task_009.txt
|
||||
tasks/task_010.txt
|
||||
tasks/tasks.json
|
||||
|
@@ -32,8 +32,8 @@ ENV NODE_ENV=development
|
||||
RUN npm install && \
|
||||
npm cache clean --force
|
||||
|
||||
# Create upload directories
|
||||
RUN mkdir -p uploads local_uploads
|
||||
# Create upload directory
|
||||
RUN mkdir -p uploads
|
||||
|
||||
# Copy source with specific paths to avoid unnecessary files
|
||||
COPY src/ ./src/
|
||||
|
122
LOCAL_DEVELOPMENT.md
Normal file
122
LOCAL_DEVELOPMENT.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Local Development (Recommended Quick Start)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Node.js** >= 20.0.0
|
||||
_Why?_: The app uses features only available in Node 20+.
|
||||
- **npm** (comes with Node.js)
|
||||
- **Python 3** (for notification testing, optional)
|
||||
- **Apprise** (for notification testing, optional)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone https://github.com/yourusername/dumbdrop.git
|
||||
cd dumbdrop
|
||||
```
|
||||
|
||||
2. **Copy and configure environment variables**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
- Open `.env` in your editor and review the variables.
|
||||
- At minimum, set:
|
||||
- `PORT=3000`
|
||||
- `LOCAL_UPLOAD_DIR=./local_uploads`
|
||||
- `MAX_FILE_SIZE=1024`
|
||||
- `DUMBDROP_PIN=` (optional, for PIN protection)
|
||||
- `APPRISE_URL=` (optional, for notifications)
|
||||
|
||||
3. **Install dependencies**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
4. **Start the development server**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
- You should see output like:
|
||||
```
|
||||
DumbDrop server running on http://localhost:3000
|
||||
```
|
||||
|
||||
5. **Open the app**
|
||||
- Go to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
---
|
||||
|
||||
## Testing File Uploads
|
||||
|
||||
- Drag and drop files onto the web interface.
|
||||
- Supported file types: _All_, unless restricted by `ALLOWED_EXTENSIONS` in `.env`.
|
||||
- Maximum file size: as set by `MAX_FILE_SIZE` (default: 1024 MB).
|
||||
- Uploaded files are stored in the directory specified by `LOCAL_UPLOAD_DIR` (default: `./local_uploads`).
|
||||
- To verify uploads:
|
||||
- Check the `local_uploads` folder for your files.
|
||||
- The UI will show a success message on upload.
|
||||
|
||||
---
|
||||
|
||||
## Notification Testing (Python/Apprise)
|
||||
|
||||
If you want to test notifications (e.g., for new uploads):
|
||||
|
||||
1. **Install Python 3**
|
||||
- [Download Python](https://www.python.org/downloads/) if not already installed.
|
||||
|
||||
2. **Install Apprise**
|
||||
```bash
|
||||
pip install apprise
|
||||
```
|
||||
|
||||
3. **Configure Apprise in `.env`**
|
||||
- Set `APPRISE_URL` to your notification service URL (see [Apprise documentation](https://github.com/caronc/apprise)).
|
||||
- Example for a local test:
|
||||
```
|
||||
APPRISE_URL=mailto://your@email.com
|
||||
```
|
||||
|
||||
4. **Trigger a test notification**
|
||||
- Upload a file via the web UI.
|
||||
- If configured, you should receive a notification.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Problem:** Port already in use
|
||||
**Solution:**
|
||||
- Change the `PORT` in `.env` to a free port.
|
||||
|
||||
**Problem:** "Cannot find module 'express'"
|
||||
**Solution:**
|
||||
- Run `npm install` to install dependencies.
|
||||
|
||||
**Problem:** File uploads not working
|
||||
**Solution:**
|
||||
- Ensure `LOCAL_UPLOAD_DIR` exists and is writable.
|
||||
- Check file size and extension restrictions in `.env`.
|
||||
|
||||
**Problem:** Notifications not sent
|
||||
**Solution:**
|
||||
- Verify `APPRISE_URL` is set and correct.
|
||||
- Ensure Apprise is installed and accessible.
|
||||
|
||||
**Problem:** Permission denied on uploads
|
||||
**Solution:**
|
||||
- Make sure your user has write permissions to `local_uploads`.
|
||||
|
||||
**Problem:** Environment variables not loading
|
||||
**Solution:**
|
||||
- Double-check that `.env` exists and is formatted correctly.
|
||||
- Restart the server after making changes.
|
||||
|
||||
---
|
||||
|
||||
## Additional Notes
|
||||
|
||||
- For Docker-based development, see the "Quick Start" and "Docker Compose" sections in the main README.
|
||||
- For more advanced configuration, review the "Configuration" section in the main README.
|
||||
- If you encounter issues not listed here, please open an issue on GitHub or check the Discussions tab.
|
101
README.md
101
README.md
@@ -8,10 +8,11 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
|
||||
|
||||
## Table of Contents
|
||||
- [Quick Start](#quick-start)
|
||||
- [Production Deployment with Docker](#production-deployment-with-docker)
|
||||
- [Local Development (Recommended Quick Start)](LOCAL_DEVELOPMENT.md)
|
||||
- [Features](#features)
|
||||
- [Configuration](#configuration)
|
||||
- [Security](#security)
|
||||
- [Development](#development)
|
||||
- [Technical Details](#technical-details)
|
||||
- [Demo Mode](demo.md)
|
||||
- [Contributing](#contributing)
|
||||
@@ -19,17 +20,13 @@ No auth (unless you want it now!), no storage, no nothing. Just a simple file up
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- Docker (recommended)
|
||||
- Node.js >=20.0.0 (for local development)
|
||||
|
||||
### Option 1: Docker (For Dummies)
|
||||
```bash
|
||||
# Pull and run with one command
|
||||
docker run -p 3000:3000 -v ./local_uploads:/app/uploads dumbwareio/dumbdrop:latest
|
||||
docker run -p 3000:3000 -v ./uploads:/app/uploads dumbwareio/dumbdrop:latest
|
||||
```
|
||||
1. Go to http://localhost:3000
|
||||
2. Upload a File - It'll show up in ./local_uploads
|
||||
2. Upload a File - It'll show up in ./uploads
|
||||
3. Celebrate on how dumb easy this was
|
||||
|
||||
### Option 2: Docker Compose (For Dummies who like customizing)
|
||||
@@ -42,8 +39,10 @@ services:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
# Where your uploaded files will land
|
||||
- ./local_uploads:/app/uploads
|
||||
- ./uploads:/app/uploads
|
||||
environment:
|
||||
# Explicitly set upload directory inside the container
|
||||
UPLOAD_DIR: /app/uploads
|
||||
# The title shown in the web interface
|
||||
DUMBDROP_TITLE: DumbDrop
|
||||
# Maximum file size in MB
|
||||
@@ -55,42 +54,21 @@ services:
|
||||
# The base URL for the application
|
||||
BASE_URL: http://localhost:3000
|
||||
```
|
||||
|
||||
Then run:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
1. Go to http://localhost:3000
|
||||
2. Upload a File - It'll show up in ./local_uploads
|
||||
2. Upload a File - It'll show up in ./uploads
|
||||
3. Rejoice in the glory of your dumb uploads
|
||||
|
||||
> **Note:** The `UPLOAD_DIR` environment variable is now explicitly set to `/app/uploads` in the container. The Dockerfile only creates the `uploads` directory, not `local_uploads`. The host directory `./uploads` is mounted to `/app/uploads` for persistent storage.
|
||||
|
||||
### Option 3: Running Locally (For Developers)
|
||||
|
||||
> If you're a developer, check out our [Dev Guide](#development) for the dumb setup.
|
||||
For local development setup, troubleshooting, and advanced usage, see the dedicated guide:
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Set environment variables in `.env`:
|
||||
```env
|
||||
PORT=3000 # Port to run the server on
|
||||
MAX_FILE_SIZE=1024 # Maximum file size in MB
|
||||
DUMBDROP_PIN=123456 # Optional PIN protection
|
||||
```
|
||||
|
||||
3. Start the server:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
#### Windows Users
|
||||
If you're using Windows PowerShell with Docker, use this format for paths:
|
||||
```bash
|
||||
docker run -p 3000:3000 -v "${PWD}\local_uploads:/app/uploads" dumbwareio/dumbdrop:latest
|
||||
```
|
||||
👉 [Local Development Guide](LOCAL_DEVELOPMENT.md)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -111,23 +89,33 @@ docker run -p 3000:3000 -v "${PWD}\local_uploads:/app/uploads" dumbwareio/dumbdr
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default | Required |
|
||||
|------------------|---------------------------------------|---------|----------|
|
||||
| PORT | Server port | 3000 | No |
|
||||
| BASE_URL | Base URL for the application | http://localhost:PORT | 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}), Storage used {storage} | No |
|
||||
| APPRISE_SIZE_UNIT| Size unit for notifications | Auto | No |
|
||||
| AUTO_UPLOAD | Enable automatic upload on file selection | false | No |
|
||||
| ALLOWED_EXTENSIONS| Comma-separated list of allowed file extensions | None | No |
|
||||
| ALLOWED_IFRAME_ORIGINS | Comma-separated list of origins allowed to embed the app in an iframe (e.g. https://organizr.example.com,https://myportal.com) | None | No |
|
||||
| Variable | Description | Default | Required |
|
||||
|------------------------|------------------------------------------------------------------|-----------------------------------------|----------|
|
||||
| PORT | Server port | 3000 | No |
|
||||
| BASE_URL | Base URL for the application | http://localhost:PORT | 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}), Storage used {storage} | No |
|
||||
| APPRISE_SIZE_UNIT | Size unit for notifications (B, KB, MB, GB, TB, or Auto) | Auto | No |
|
||||
| AUTO_UPLOAD | Enable automatic upload on file selection | false | No |
|
||||
| ALLOWED_EXTENSIONS | Comma-separated list of allowed file extensions | None | No |
|
||||
| ALLOWED_IFRAME_ORIGINS | Comma-separated list of origins allowed to embed the app in an iframe | None | No |
|
||||
| UPLOAD_DIR | Directory for uploads (Docker/production; should be `/app/uploads` in container) | None (see LOCAL_UPLOAD_DIR fallback) | No |
|
||||
| LOCAL_UPLOAD_DIR | Directory for uploads (local dev, fallback: './local_uploads') | ./local_uploads | No |
|
||||
|
||||
### ALLOWED_IFRAME_ORIGINS
|
||||
- **UPLOAD_DIR** is used in Docker/production. If not set, LOCAL_UPLOAD_DIR is used for local development. If neither is set, the default is `./local_uploads`.
|
||||
- **Docker Note:** The Dockerfile now only creates the `uploads` directory inside the container. The host's `./local_uploads` is mounted to `/app/uploads` and should be managed on the host system.
|
||||
- **BASE_URL**: If you are deploying DumbDrop under a subpath (e.g., `https://example.com/watchfolder/`), you **must** set `BASE_URL` to the full path including the trailing slash (e.g., `https://example.com/watchfolder/`). All API and asset requests will be prefixed with this value. If you deploy at the root, use `https://example.com/`.
|
||||
- **BASE_URL** must end with a trailing slash. The app will fail to start if this is not the case.
|
||||
|
||||
To allow this app to be embedded in an iframe on specific origins (such as Organizr), set the `ALLOWED_IFRAME_ORIGINS` environment variable to a comma-separated list of allowed parent origins. Example:
|
||||
See `.env.example` for a template and more details.
|
||||
|
||||
<details>
|
||||
<summary>ALLOWED_IFRAME_ORIGINS</summary>
|
||||
|
||||
To allow this app to be embedded in an iframe on specific origins (such as Organizr), set the `ALLOWED_IFRAME_ORIGINS` environment variable. For example:
|
||||
|
||||
```env
|
||||
ALLOWED_IFRAME_ORIGINS=https://organizr.example.com,https://myportal.com
|
||||
@@ -136,15 +124,20 @@ ALLOWED_IFRAME_ORIGINS=https://organizr.example.com,https://myportal.com
|
||||
- If not set, the app will only allow itself to be embedded in an iframe on the same origin (default security).
|
||||
- If set, the app will allow embedding in iframes on the specified origins and itself.
|
||||
- **Security Note:** Only add trusted origins. Allowing arbitrary origins can expose your app to clickjacking and other attacks.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>File Extension Filtering</summary>
|
||||
|
||||
### File Extension Filtering
|
||||
To restrict which file types can be uploaded, set the `ALLOWED_EXTENSIONS` environment variable. For example:
|
||||
```env
|
||||
ALLOWED_EXTENSIONS=.jpg,.jpeg,.png,.pdf,.doc,.docx,.txt
|
||||
```
|
||||
If not set, all file extensions will be allowed.
|
||||
</details>
|
||||
|
||||
### Notification Setup
|
||||
<details>
|
||||
<summary>Notification Setup</summary>
|
||||
|
||||
#### Message Templates
|
||||
The notification message supports the following placeholders:
|
||||
@@ -168,6 +161,7 @@ Both {size} and {storage} use the same formatting rules based on APPRISE_SIZE_UN
|
||||
- Support for all Apprise notification services
|
||||
- Customizable notification messages with filename templating
|
||||
- Optional - disabled if no APPRISE_URL is set
|
||||
</details>
|
||||
|
||||
## Security
|
||||
|
||||
@@ -206,10 +200,7 @@ Both {size} and {storage} use the same formatting rules based on APPRISE_SIZE_UN
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
See [Development Guide](dev/README.md) for local setup and guidelines.
|
||||
|
||||
|
||||
|
||||
See [Local Development (Recommended Quick Start)](LOCAL_DEVELOPMENT.md) for local setup and guidelines.
|
||||
|
||||
---
|
||||
Made with ❤️ by [DumbWare.io](https://dumbware.io)
|
||||
|
@@ -1,50 +0,0 @@
|
||||
# Version control
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Development
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Build outputs
|
||||
dist
|
||||
build
|
||||
coverage
|
||||
|
||||
# Local uploads (development only)
|
||||
local_uploads
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Docker
|
||||
.docker
|
||||
docker-compose*.yml
|
||||
Dockerfile*
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
docs
|
||||
|
||||
# Development configurations
|
||||
.editorconfig
|
||||
nodemon.json
|
@@ -1,22 +0,0 @@
|
||||
# Development Environment Settings
|
||||
|
||||
# Server Configuration
|
||||
PORT=3000 # Development server port
|
||||
|
||||
# Upload Settings
|
||||
MAX_FILE_SIZE=1024 # Maximum file size in MB for development
|
||||
AUTO_UPLOAD=false # Disable auto-upload by default in development
|
||||
UPLOAD_DIR=../local_uploads # Local development upload directory
|
||||
|
||||
# Development Specific
|
||||
DUMBDROP_TITLE=DumbDrop-Dev # Development environment indicator
|
||||
DUMBDROP_PIN=123456 # Default development PIN (change in production)
|
||||
|
||||
# Optional Development Features
|
||||
NODE_ENV=development # Ensures development mode
|
||||
DEBUG=dumbdrop:* # Enable debug logging (if implemented)
|
||||
|
||||
# Development Notifications (Optional)
|
||||
APPRISE_URL= # Test notification endpoint
|
||||
APPRISE_MESSAGE=[DEV] New file uploaded - {filename} ({size}), Storage used {storage}
|
||||
APPRISE_SIZE_UNIT=auto
|
@@ -1,46 +0,0 @@
|
||||
# Base stage for shared configurations
|
||||
FROM node:20-alpine as base
|
||||
|
||||
# Install python and create virtual environment with minimal dependencies
|
||||
RUN apk add --no-cache python3 py3-pip && \
|
||||
python3 -m venv /opt/venv && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
# Activate virtual environment and install apprise
|
||||
RUN . /opt/venv/bin/activate && \
|
||||
pip install --no-cache-dir apprise && \
|
||||
find /opt/venv -type d -name "__pycache__" -exec rm -r {} +
|
||||
|
||||
# Add virtual environment to PATH
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Dependencies stage
|
||||
FROM base as deps
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production && \
|
||||
npm cache clean --force
|
||||
|
||||
# Development stage
|
||||
FROM deps as development
|
||||
ENV NODE_ENV=development
|
||||
|
||||
# Install dev dependencies
|
||||
RUN npm install && \
|
||||
npm cache clean --force
|
||||
|
||||
# Create upload directories
|
||||
RUN mkdir -p uploads local_uploads
|
||||
|
||||
# Copy source with specific paths to avoid unnecessary files
|
||||
COPY src/ ./src/
|
||||
COPY public/ ./public/
|
||||
COPY dev/ ./dev/
|
||||
COPY .eslintrc.json .eslintignore ./
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
@@ -1,73 +0,0 @@
|
||||
# DumbDrop Development Guide
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/DumbDrop.git
|
||||
cd DumbDrop
|
||||
```
|
||||
|
||||
2. Set up development environment:
|
||||
```bash
|
||||
cd dev
|
||||
cp .env.dev.example .env.dev
|
||||
```
|
||||
|
||||
3. Start development server:
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
The application will be available at http://localhost:3000 with hot-reloading enabled.
|
||||
|
||||
## Development Environment Features
|
||||
|
||||
- Hot-reloading with nodemon
|
||||
- Development-specific environment variables
|
||||
- Local file storage in `../local_uploads`
|
||||
- Debug logging enabled
|
||||
- Development-specific notifications
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
DumbDrop/
|
||||
├── dev/ # Development configurations
|
||||
│ ├── docker-compose.dev.yml
|
||||
│ ├── .env.dev.example
|
||||
│ └── README.md
|
||||
├── src/ # Application source code
|
||||
├── public/ # Static assets
|
||||
├── local_uploads/ # Development file storage
|
||||
└── [Production files in root]
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. Create feature branches from `main`:
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
2. Make changes and test locally
|
||||
3. Commit using conventional commits:
|
||||
```bash
|
||||
feat: add new feature
|
||||
fix: resolve bug
|
||||
docs: update documentation
|
||||
```
|
||||
|
||||
4. Push and create pull request
|
||||
|
||||
## Debugging
|
||||
|
||||
- Use `DEBUG=dumbdrop:*` for detailed logs
|
||||
- Container shell access: `docker-compose -f docker-compose.dev.yml exec app sh`
|
||||
- Logs: `docker-compose -f docker-compose.dev.yml logs -f app`
|
||||
|
||||
## Common Issues
|
||||
|
||||
1. Port conflicts: Change port in `.env.dev`
|
||||
2. File permissions: Ensure proper ownership of `local_uploads`
|
||||
3. Node modules: Remove and rebuild with `docker-compose -f docker-compose.dev.yml build --no-cache`
|
74
dev/dev.sh
74
dev/dev.sh
@@ -1,74 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set script to exit on error
|
||||
set -e
|
||||
|
||||
# Enable Docker BuildKit
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
# Colors for pretty output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Helper function for pretty printing
|
||||
print_message() {
|
||||
echo -e "${BLUE}🔧 ${1}${NC}"
|
||||
}
|
||||
|
||||
# Ensure we're in the right directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
case "$1" in
|
||||
"up")
|
||||
print_message "Starting DumbDrop in development mode..."
|
||||
if [ ! -f .env.dev ]; then
|
||||
print_message "No .env.dev found. Creating from example..."
|
||||
cp .env.dev.example .env.dev
|
||||
fi
|
||||
docker compose -f docker-compose.dev.yml up -d --build
|
||||
print_message "Container logs:"
|
||||
docker compose -f docker-compose.dev.yml logs
|
||||
;;
|
||||
"down")
|
||||
print_message "Stopping DumbDrop development environment..."
|
||||
docker compose -f docker-compose.dev.yml down
|
||||
;;
|
||||
"logs")
|
||||
print_message "Showing DumbDrop logs..."
|
||||
docker compose -f docker-compose.dev.yml logs -f
|
||||
;;
|
||||
"rebuild")
|
||||
print_message "Rebuilding DumbDrop..."
|
||||
docker compose -f docker-compose.dev.yml build --no-cache
|
||||
docker compose -f docker-compose.dev.yml up
|
||||
;;
|
||||
"clean")
|
||||
print_message "Cleaning up development environment..."
|
||||
docker compose -f docker-compose.dev.yml down -v --remove-orphans
|
||||
rm -f .env.dev
|
||||
print_message "Cleaned up containers, volumes, and env file"
|
||||
;;
|
||||
"shell")
|
||||
print_message "Opening shell in container..."
|
||||
docker compose -f docker-compose.dev.yml exec app sh
|
||||
;;
|
||||
"lint")
|
||||
print_message "Running linter..."
|
||||
docker compose -f docker-compose.dev.yml exec app npm run lint
|
||||
;;
|
||||
*)
|
||||
echo -e "${GREEN}DumbDrop Development Helper${NC}"
|
||||
echo "Usage: ./dev.sh [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " up - Start development environment (creates .env.dev if missing)"
|
||||
echo " down - Stop development environment"
|
||||
echo " logs - Show container logs"
|
||||
echo " rebuild - Rebuild container without cache and start"
|
||||
echo " clean - Clean up everything (containers, volumes, env)"
|
||||
echo " shell - Open shell in container"
|
||||
echo " lint - Run linter"
|
||||
;;
|
||||
esac
|
@@ -1,33 +0,0 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: dev/Dockerfile.dev
|
||||
target: development
|
||||
args:
|
||||
DOCKER_BUILDKIT: 1
|
||||
x-bake:
|
||||
options:
|
||||
dockerignore: dev/.dockerignore
|
||||
volumes:
|
||||
- ..:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- PORT=3000
|
||||
- MAX_FILE_SIZE=1024
|
||||
- AUTO_UPLOAD=false
|
||||
- DUMBDROP_TITLE=DumbDrop-Dev
|
||||
# - APPRISE_URL=ntfy://dumbdrop-test
|
||||
# - APPRISE_MESSAGE=[DEV] New file uploaded - {filename} ({size}), Storage used {storage}
|
||||
# - APPRISE_SIZE_UNIT=auto
|
||||
command: npm run dev
|
||||
restart: unless-stopped
|
||||
# Enable container debugging if needed
|
||||
# stdin_open: true
|
||||
# tty: true
|
||||
# Add development labels
|
||||
labels:
|
||||
- "dev.dumbware.environment=development"
|
@@ -7,6 +7,8 @@ services:
|
||||
# Replace "./local_uploads" ( before the colon ) with the path where the files land
|
||||
- ./local_uploads:/app/uploads
|
||||
environment: # Environment variables for the DumbDrop service
|
||||
# Explicitly set upload directory inside the container
|
||||
UPLOAD_DIR: /app/uploads
|
||||
DUMBDROP_TITLE: DumbDrop # The title shown in the web interface
|
||||
MAX_FILE_SIZE: 1024 # Maximum file size in MB
|
||||
DUMBDROP_PIN: 123456 # Optional PIN protection (4-10 digits, leave empty to disable)
|
||||
|
432
package-lock.json
generated
432
package-lock.json
generated
@@ -29,9 +29,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
|
||||
"integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -81,31 +81,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
|
||||
@@ -132,31 +107,6 @@
|
||||
"node": ">=10.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array/node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
@@ -238,9 +188,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -390,6 +340,21 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -441,9 +406,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
@@ -454,13 +419,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
|
||||
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"get-intrinsic": "^1.2.6"
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -496,29 +461,6 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -544,6 +486,19 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -608,9 +563,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -629,15 +584,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser/node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@@ -679,12 +625,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
@@ -727,9 +682,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.7",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -935,16 +890,6 @@
|
||||
"eslint": ">=5.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-node/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
|
||||
@@ -1001,44 +946,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
|
||||
@@ -1173,6 +1080,30 @@
|
||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -1195,9 +1126,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
|
||||
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
@@ -1248,6 +1179,21 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
@@ -1281,9 +1227,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
|
||||
"integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -1337,17 +1283,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
@@ -1396,16 +1342,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
"is-glob": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
@@ -1444,13 +1390,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
@@ -1846,15 +1792,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "1.4.5-lts.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||
"version": "1.4.5-lts.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
|
||||
"integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
@@ -1886,9 +1832,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
|
||||
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
|
||||
"integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1914,31 +1860,42 @@
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"node_modules/nodemon/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -1959,9 +1916,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
|
||||
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2130,9 +2087,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
||||
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -2320,9 +2277,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2398,16 +2355,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
@@ -2434,6 +2388,21 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@@ -2443,12 +2412,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
@@ -2578,6 +2541,19 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@@ -2637,16 +2613,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
|
@@ -4,10 +4,11 @@
|
||||
"main": "src/server.js",
|
||||
"scripts": {
|
||||
"start": "node src/server.js",
|
||||
"dev": "nodemon --legacy-watch src/server.js",
|
||||
"dev": "nodemon src/server.js",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"format": "prettier --write ."
|
||||
"format": "prettier --write .",
|
||||
"predev": "node -e \"const v=process.versions.node.split('.');if(v[0]<20) {console.error('Node.js >=20.0.0 required');process.exit(1)}\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
@@ -4,11 +4,12 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{SITE_TITLE}} - Simple File Upload</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="{{BASE_URL}}styles.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>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" type="image/svg+xml" href="assets/icon.svg">
|
||||
<link rel="manifest" href="{{BASE_URL}}manifest.json">
|
||||
<link rel="icon" type="image/svg+xml" href="{{BASE_URL}}assets/icon.svg">
|
||||
<script>window.BASE_URL = '{{BASE_URL}}';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -116,7 +117,9 @@
|
||||
headers['X-Batch-ID'] = this.batchId;
|
||||
}
|
||||
|
||||
const response = await fetch('/api/upload/init', {
|
||||
// Remove leading slash from API path before concatenating
|
||||
const apiUrl = '/api/upload/init'.startsWith('/') ? '/api/upload/init'.substring(1) : '/api/upload/init';
|
||||
const response = await fetch(window.BASE_URL + apiUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
@@ -152,7 +155,10 @@
|
||||
}
|
||||
|
||||
async uploadChunk(chunk) {
|
||||
const response = await fetch(`/api/upload/chunk/${this.uploadId}`, {
|
||||
// Remove leading slash from API path before concatenating
|
||||
const chunkApiUrlPath = `/api/upload/chunk/${this.uploadId}`;
|
||||
const chunkApiUrl = chunkApiUrlPath.startsWith('/') ? chunkApiUrlPath.substring(1) : chunkApiUrlPath;
|
||||
const response = await fetch(window.BASE_URL + chunkApiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
@@ -824,6 +830,17 @@
|
||||
const savedTheme = localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
setTheme(savedTheme);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Rewrite asset URLs to use BASE_URL as prefix if not absolute
|
||||
const baseUrl = window.BASE_URL;
|
||||
document.querySelectorAll('link[rel="stylesheet"], link[rel="manifest"], link[rel="icon"]').forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && !href.startsWith('http') && !href.startsWith('data:') && !href.startsWith(baseUrl)) {
|
||||
link.setAttribute('href', baseUrl + href.replace(/^\//, ''));
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{SITE_TITLE}} - Login</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="icon" type="image/svg+xml" href="assets/icon.svg">
|
||||
<link rel="stylesheet" href="{{BASE_URL}}styles.css">
|
||||
<link rel="icon" type="image/svg+xml" href="{{BASE_URL}}assets/icon.svg">
|
||||
<style>
|
||||
.login-container {
|
||||
display: flex;
|
||||
@@ -54,6 +54,7 @@
|
||||
background-color: var(--textarea-bg);
|
||||
}
|
||||
</style>
|
||||
<script>window.BASE_URL = '{{BASE_URL}}';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
@@ -125,7 +126,7 @@
|
||||
// Handle form submission
|
||||
const verifyPin = async (pin) => {
|
||||
try {
|
||||
const response = await fetch('/api/auth/verify-pin', {
|
||||
const response = await fetch(window.BASE_URL + 'api/auth/verify-pin', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pin })
|
||||
@@ -211,7 +212,7 @@
|
||||
};
|
||||
|
||||
// Check PIN length and initialize
|
||||
fetch('/api/auth/pin-required')
|
||||
fetch(window.BASE_URL + 'api/auth/pin-required')
|
||||
.then(response => {
|
||||
if (response.status === 429) {
|
||||
throw new Error('Too many attempts. Please wait before trying again.');
|
||||
@@ -240,6 +241,17 @@
|
||||
pinContainer.style.pointerEvents = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Rewrite asset URLs to use BASE_URL as prefix if not absolute
|
||||
const baseUrl = window.BASE_URL;
|
||||
document.querySelectorAll('link[rel="stylesheet"], link[rel="icon"]').forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href && !href.startsWith('http') && !href.startsWith('data:') && !href.startsWith(baseUrl)) {
|
||||
link.setAttribute('href', baseUrl + href.replace(/^\//, ''));
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
11
src/app.js
11
src/app.js
@@ -53,6 +53,9 @@ app.get('/', (req, res) => {
|
||||
let html = fs.readFileSync(path.join(__dirname, '../public', 'index.html'), 'utf8');
|
||||
html = html.replace(/{{SITE_TITLE}}/g, config.siteTitle);
|
||||
html = html.replace('{{AUTO_UPLOAD}}', config.autoUpload.toString());
|
||||
// Ensure baseUrl has a trailing slash for correct asset linking
|
||||
const baseUrlWithSlash = config.baseUrl.endsWith('/') ? config.baseUrl : config.baseUrl + '/';
|
||||
html = html.replace(/{{BASE_URL}}/g, baseUrlWithSlash);
|
||||
html = injectDemoBanner(html);
|
||||
res.send(html);
|
||||
});
|
||||
@@ -66,6 +69,9 @@ app.get('/login.html', (req, res) => {
|
||||
|
||||
let html = fs.readFileSync(path.join(__dirname, '../public', 'login.html'), 'utf8');
|
||||
html = html.replace(/{{SITE_TITLE}}/g, config.siteTitle);
|
||||
// Ensure baseUrl has a trailing slash
|
||||
const baseUrlWithSlash = config.baseUrl.endsWith('/') ? config.baseUrl : config.baseUrl + '/';
|
||||
html = html.replace(/{{BASE_URL}}/g, baseUrlWithSlash);
|
||||
html = injectDemoBanner(html);
|
||||
res.send(html);
|
||||
});
|
||||
@@ -80,9 +86,12 @@ app.use((req, res, next) => {
|
||||
const filePath = path.join(__dirname, '../public', req.path);
|
||||
let html = fs.readFileSync(filePath, 'utf8');
|
||||
html = html.replace(/{{SITE_TITLE}}/g, config.siteTitle);
|
||||
if (req.path === 'index.html') {
|
||||
if (req.path === '/index.html' || req.path === 'index.html') {
|
||||
html = html.replace('{{AUTO_UPLOAD}}', config.autoUpload.toString());
|
||||
}
|
||||
// Ensure baseUrl has a trailing slash
|
||||
const baseUrlWithSlash = config.baseUrl.endsWith('/') ? config.baseUrl : config.baseUrl + '/';
|
||||
html = html.replace(/{{BASE_URL}}/g, baseUrlWithSlash);
|
||||
html = injectDemoBanner(html);
|
||||
res.send(html);
|
||||
} catch (err) {
|
||||
|
@@ -1,48 +1,130 @@
|
||||
require('dotenv').config();
|
||||
console.log('Loaded ENV:', {
|
||||
PORT: process.env.PORT,
|
||||
UPLOAD_DIR: process.env.UPLOAD_DIR,
|
||||
LOCAL_UPLOAD_DIR: process.env.LOCAL_UPLOAD_DIR,
|
||||
NODE_ENV: process.env.NODE_ENV
|
||||
});
|
||||
const { validatePin } = require('../utils/security');
|
||||
const logger = require('../utils/logger');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Get the host path from Docker mount point
|
||||
* @returns {string} Host path or fallback to container path
|
||||
* Environment Variables Reference
|
||||
*
|
||||
* PORT - Port for the server (default: 3000)
|
||||
* NODE_ENV - Node environment (default: 'development')
|
||||
* BASE_URL - Base URL for the app (default: http://localhost:${PORT})
|
||||
* UPLOAD_DIR - Directory for uploads (Docker/production)
|
||||
* LOCAL_UPLOAD_DIR - Directory for uploads (local dev, fallback: './local_uploads')
|
||||
* MAX_FILE_SIZE - Max upload size in MB (default: 1024)
|
||||
* AUTO_UPLOAD - Enable auto-upload (true/false, default: false)
|
||||
* DUMBDROP_PIN - Security PIN for uploads (required for protected endpoints)
|
||||
* DUMBDROP_TITLE - Site title (default: 'DumbDrop')
|
||||
* APPRISE_URL - Apprise notification URL (optional)
|
||||
* APPRISE_MESSAGE - Notification message template (default provided)
|
||||
* APPRISE_SIZE_UNIT - Size unit for notifications (optional)
|
||||
* ALLOWED_EXTENSIONS - Comma-separated list of allowed file extensions (optional)
|
||||
* ALLOWED_IFRAME_ORIGINS - Comma-separated list of allowed iframe origins (optional)
|
||||
*/
|
||||
function getHostPath() {
|
||||
|
||||
// Helper for clear configuration logging
|
||||
const logConfig = (message, level = 'info') => {
|
||||
const prefix = level === 'warning' ? '⚠️ WARNING:' : 'ℹ️ INFO:';
|
||||
console.log(`${prefix} CONFIGURATION: ${message}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine the upload directory based on environment variables.
|
||||
* Priority:
|
||||
* 1. UPLOAD_DIR (for Docker/production)
|
||||
* 2. LOCAL_UPLOAD_DIR (for local development)
|
||||
* 3. './local_uploads' (default fallback)
|
||||
* @returns {string} The upload directory path
|
||||
*/
|
||||
function determineUploadDirectory() {
|
||||
let uploadDir;
|
||||
if (process.env.UPLOAD_DIR) {
|
||||
uploadDir = process.env.UPLOAD_DIR;
|
||||
logConfig(`Upload directory set from UPLOAD_DIR: ${uploadDir}`);
|
||||
} else if (process.env.LOCAL_UPLOAD_DIR) {
|
||||
uploadDir = process.env.LOCAL_UPLOAD_DIR;
|
||||
logConfig(`Upload directory using LOCAL_UPLOAD_DIR fallback: ${uploadDir}`, 'warning');
|
||||
} else {
|
||||
uploadDir = './local_uploads';
|
||||
logConfig(`Upload directory using default fallback: ${uploadDir}`, 'warning');
|
||||
}
|
||||
logConfig(`Final upload directory path: ${require('path').resolve(uploadDir)}`);
|
||||
return uploadDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to detect if running in local development mode
|
||||
* Returns true if NODE_ENV is not 'production' and UPLOAD_DIR is not set (i.e., not Docker)
|
||||
*/
|
||||
function isLocalDevelopment() {
|
||||
return process.env.NODE_ENV !== 'production' && !process.env.UPLOAD_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the upload directory exists (for local development only)
|
||||
* Creates the directory if it does not exist
|
||||
*/
|
||||
function ensureLocalUploadDirExists(uploadDir) {
|
||||
if (!isLocalDevelopment()) return;
|
||||
try {
|
||||
// Read Docker mountinfo to get the host path
|
||||
const mountInfo = fs.readFileSync('/proc/self/mountinfo', 'utf8');
|
||||
const lines = mountInfo.split('\n');
|
||||
|
||||
// Find the line containing our upload directory
|
||||
const uploadMount = lines.find(line => line.includes('/app/uploads'));
|
||||
if (uploadMount) {
|
||||
// Extract the host path from the mount info
|
||||
const parts = uploadMount.split(' ');
|
||||
// The host path is typically in the 4th space-separated field
|
||||
const hostPath = parts[3];
|
||||
return hostPath;
|
||||
if (!fs.existsSync(uploadDir)) {
|
||||
fs.mkdirSync(uploadDir, { recursive: true });
|
||||
logConfig(`Created local upload directory: ${uploadDir}`);
|
||||
} else {
|
||||
logConfig(`Local upload directory exists: ${uploadDir}`);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.debug('Could not determine host path from mount info');
|
||||
logConfig(`Failed to create local upload directory: ${uploadDir}. Error: ${err.message}`, 'warning');
|
||||
}
|
||||
|
||||
// Fallback to container path if we can't determine host path
|
||||
return '/app/uploads';
|
||||
}
|
||||
|
||||
// Determine and ensure upload directory (for local dev)
|
||||
const resolvedUploadDir = determineUploadDirectory();
|
||||
ensureLocalUploadDirExists(resolvedUploadDir);
|
||||
|
||||
/**
|
||||
* Application configuration
|
||||
* Loads and validates environment variables
|
||||
*/
|
||||
const config = {
|
||||
// =====================
|
||||
// Server settings
|
||||
// =====================
|
||||
/**
|
||||
* Port for the server (default: 3000)
|
||||
* Set via PORT in .env
|
||||
*/
|
||||
port: process.env.PORT || 3000,
|
||||
/**
|
||||
* Node environment (default: 'development')
|
||||
* Set via NODE_ENV in .env
|
||||
*/
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
/**
|
||||
* Base URL for the app (default: http://localhost:${PORT})
|
||||
* Set via BASE_URL in .env
|
||||
*/
|
||||
baseUrl: process.env.BASE_URL || `http://localhost:${process.env.PORT || 3000}`,
|
||||
|
||||
// =====================
|
||||
// Upload settings
|
||||
uploadDir: '/app/uploads', // Internal Docker path
|
||||
uploadDisplayPath: getHostPath(), // Dynamically determined from Docker mount
|
||||
// =====================
|
||||
/**
|
||||
* Directory for uploads
|
||||
* Priority: UPLOAD_DIR (Docker/production) > LOCAL_UPLOAD_DIR (local dev) > './local_uploads' (fallback)
|
||||
*/
|
||||
uploadDir: resolvedUploadDir,
|
||||
/**
|
||||
* Max upload size in bytes (default: 1024MB)
|
||||
* Set via MAX_FILE_SIZE in .env (in MB)
|
||||
*/
|
||||
maxFileSize: (() => {
|
||||
const sizeInMB = parseInt(process.env.MAX_FILE_SIZE || '1024', 10);
|
||||
if (isNaN(sizeInMB) || sizeInMB <= 0) {
|
||||
@@ -50,31 +132,67 @@ const config = {
|
||||
}
|
||||
return sizeInMB * 1024 * 1024; // Convert MB to bytes
|
||||
})(),
|
||||
/**
|
||||
* Enable auto-upload (true/false, default: false)
|
||||
* Set via AUTO_UPLOAD in .env
|
||||
*/
|
||||
autoUpload: process.env.AUTO_UPLOAD === 'true',
|
||||
|
||||
// =====================
|
||||
// Security
|
||||
// =====================
|
||||
/**
|
||||
* Security PIN for uploads (required for protected endpoints)
|
||||
* Set via DUMBDROP_PIN in .env
|
||||
*/
|
||||
pin: validatePin(process.env.DUMBDROP_PIN),
|
||||
|
||||
// =====================
|
||||
// UI settings
|
||||
// =====================
|
||||
/**
|
||||
* Site title (default: 'DumbDrop')
|
||||
* Set via DUMBDROP_TITLE in .env
|
||||
*/
|
||||
siteTitle: process.env.DUMBDROP_TITLE || 'DumbDrop',
|
||||
|
||||
// =====================
|
||||
// Notification settings
|
||||
// =====================
|
||||
/**
|
||||
* Apprise notification URL (optional)
|
||||
* Set via APPRISE_URL in .env
|
||||
*/
|
||||
appriseUrl: process.env.APPRISE_URL,
|
||||
/**
|
||||
* Notification message template (default provided)
|
||||
* Set via APPRISE_MESSAGE in .env
|
||||
*/
|
||||
appriseMessage: process.env.APPRISE_MESSAGE || 'New file uploaded - {filename} ({size}), Storage used {storage}',
|
||||
/**
|
||||
* Size unit for notifications (optional)
|
||||
* Set via APPRISE_SIZE_UNIT in .env
|
||||
*/
|
||||
appriseSizeUnit: process.env.APPRISE_SIZE_UNIT,
|
||||
|
||||
// =====================
|
||||
// File extensions
|
||||
// =====================
|
||||
/**
|
||||
* Allowed file extensions (comma-separated, optional)
|
||||
* Set via ALLOWED_EXTENSIONS in .env
|
||||
*/
|
||||
allowedExtensions: process.env.ALLOWED_EXTENSIONS ?
|
||||
process.env.ALLOWED_EXTENSIONS.split(',').map(ext => ext.trim().toLowerCase()) :
|
||||
null,
|
||||
|
||||
// Allowed iframe origins (for embedding in iframes)
|
||||
// Comma-separated list of origins, e.g. "https://organizr.example.com,https://dumb.myportal.com"
|
||||
allowedIframeOrigins: process.env.ALLOWED_IFRAME_ORIGINS
|
||||
? process.env.ALLOWED_IFRAME_ORIGINS.split(',').map(origin => origin.trim()).filter(Boolean)
|
||||
: null
|
||||
};
|
||||
|
||||
console.log(`Upload directory configured as: ${config.uploadDir}`);
|
||||
|
||||
// Validate required settings
|
||||
function validateConfig() {
|
||||
const errors = [];
|
||||
@@ -85,7 +203,12 @@ function validateConfig() {
|
||||
|
||||
// Validate BASE_URL format
|
||||
try {
|
||||
new URL(config.baseUrl);
|
||||
let url = new URL(config.baseUrl);
|
||||
// Ensure BASE_URL ends with a slash
|
||||
if (!config.baseUrl.endsWith('/')) {
|
||||
logger.warn('BASE_URL did not end with a trailing slash. Automatically appending "/".');
|
||||
config.baseUrl = config.baseUrl + '/';
|
||||
}
|
||||
} catch (err) {
|
||||
errors.push('BASE_URL must be a valid URL');
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ function securityHeaders(req, res, next) {
|
||||
// Content Security Policy
|
||||
let csp =
|
||||
"default-src 'self'; " +
|
||||
"connect-src 'self'; " +
|
||||
"style-src 'self' 'unsafe-inline' cdn.jsdelivr.net; " +
|
||||
"script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; " +
|
||||
"img-src 'self' data: blob:;";
|
||||
|
@@ -53,13 +53,16 @@ async function startServer() {
|
||||
});
|
||||
|
||||
// Shutdown handler function
|
||||
let isShuttingDown = false; // Prevent multiple shutdowns
|
||||
const shutdownHandler = async (signal) => {
|
||||
if (isShuttingDown) return;
|
||||
isShuttingDown = true;
|
||||
logger.info(`${signal} received. Shutting down gracefully...`);
|
||||
|
||||
// Start a shorter force shutdown timer
|
||||
const forceShutdownTimer = setTimeout(() => {
|
||||
logger.error('Force shutdown initiated');
|
||||
throw new Error('Force shutdown due to timeout');
|
||||
process.exit(1);
|
||||
}, 3000); // 3 seconds maximum for total shutdown
|
||||
|
||||
try {
|
||||
@@ -92,9 +95,10 @@ async function startServer() {
|
||||
// Clear the force shutdown timer since we completed gracefully
|
||||
clearTimeout(forceShutdownTimer);
|
||||
process.exitCode = 0;
|
||||
process.exit(0); // Ensure immediate exit
|
||||
} catch (error) {
|
||||
logger.error(`Error during shutdown: ${error.message}`);
|
||||
throw error;
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -9,19 +9,6 @@ const path = require('path');
|
||||
const logger = require('./logger');
|
||||
const { config } = require('../config');
|
||||
|
||||
/**
|
||||
* Get display path for logs
|
||||
* @param {string} internalPath - Internal Docker path
|
||||
* @returns {string} Display path for host machine
|
||||
*/
|
||||
function getDisplayPath(internalPath) {
|
||||
if (!internalPath.startsWith(config.uploadDir)) return internalPath;
|
||||
|
||||
// Replace the container path with the host path
|
||||
const relativePath = path.relative(config.uploadDir, internalPath);
|
||||
return path.join(config.uploadDisplayPath, relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format file size to human readable format
|
||||
* @param {number} bytes - Size in bytes
|
||||
@@ -90,13 +77,13 @@ async function ensureDirectoryExists(directoryPath) {
|
||||
try {
|
||||
if (!fs.existsSync(directoryPath)) {
|
||||
await fs.promises.mkdir(directoryPath, { recursive: true });
|
||||
logger.info(`Created directory: ${getDisplayPath(directoryPath)}`);
|
||||
logger.info(`Created directory: ${directoryPath}`);
|
||||
}
|
||||
await fs.promises.access(directoryPath, fs.constants.W_OK);
|
||||
logger.success(`Directory is writable: ${getDisplayPath(directoryPath)}`);
|
||||
logger.success(`Directory is writable: ${directoryPath}`);
|
||||
} catch (err) {
|
||||
logger.error(`Directory error: ${err.message}`);
|
||||
throw new Error(`Failed to access or create directory: ${getDisplayPath(directoryPath)}`);
|
||||
throw new Error(`Failed to access or create directory: ${directoryPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +116,8 @@ async function getUniqueFilePath(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Log using display path
|
||||
logger.info(`Using unique path: ${getDisplayPath(finalPath)}`);
|
||||
// Log using actual path
|
||||
logger.info(`Using unique path: ${finalPath}`);
|
||||
return { path: finalPath, handle: fileHandle };
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user