diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5da78d4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY scripts/package.json ./ + +RUN npm install + +COPY scripts/upload-server.js ./ + +RUN mkdir -p uploads + +EXPOSE 3000 + +CMD ["npm", "start"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 6a26d88..8810c3b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -94,38 +94,20 @@ services: - webapi - webui - seq + - upload-server networks: - app-network - zipline: - image: ghcr.io/diced/zipline:latest - expose: - - "3000" + upload-server: + build: . + ports: + - "3000:3000" environment: - - CORE_RETURN_HTTPS=true - - CORE_SECRET=${ZIPLINE_SECRET} - - CORE_HOST=0.0.0.0 - - CORE_PORT=3000 - - CORE_DATABASE_URL=file:./zipline.db - - CORE_LOGGER=true - - URLS_ROUTE=${ZIPLINE_ROUTE} - - URLS_LENGTH=6 - - UPLOADER_DEFAULT_FORMAT=RANDOM - - UPLOADER_DISABLED_EXTENSIONS= - - UPLOADER_FORMAT_DATE=%Y-%m-%d_%H-%M-%S - - FEATURES_INVITES=false - - FEATURES_REGISTRATION=false - - FEATURES_HEADLESS=true - - FEATURES_USER_REGISTRATION=false - - FEATURES_OAUTH_REGISTRATION=false - - FEATURES_ANONYMOUS_UPLOAD=true - - WEBSITE_TITLE=File Upload - - WEBSITE_EXTERNAL_LINKS=${ZIPLINE_URL} + - BASE_URL=https://impr.ink + - HASH_FILENAME=true + - UPLOAD_DIR=/app/uploads volumes: - - zipline_data:/zipline/uploads - - zipline_public:/zipline/public - - zipline_db:/zipline - restart: unless-stopped + - ./uploads:/app/uploads networks: - app-network @@ -134,6 +116,4 @@ networks: driver: bridge volumes: - zipline_data: - zipline_public: - zipline_db: \ No newline at end of file + upload_data: \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 8d2dbb1..c4bc316 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -21,8 +21,8 @@ http { server seq:80; } - upstream zipline { - server zipline:3000; + upstream upload-server { + server upload-server:3000; } server { @@ -85,23 +85,38 @@ http { rewrite ^/seq/(.*)$ /$1 break; } - location /res { - proxy_pass http://zipline; + location /upload { + proxy_pass http://upload-server/upload; proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - proxy_set_header X-Forwarded-Prefix /res; - + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_request_buffering off; proxy_buffering off; proxy_read_timeout 300s; proxy_send_timeout 300s; - - rewrite ^/res/(.*)$ /$1 break; - rewrite ^/res$ / break; + + client_max_body_size 1G; + } + + location ~ ^/files/(.+)$ { + proxy_pass http://upload-server/files/$1; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + proxy_request_buffering off; + proxy_buffering off; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + + client_max_body_size 1G; } location / { @@ -113,4 +128,4 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } } -} +} \ No newline at end of file diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..bb356f8 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,13 @@ +{ + "name": "upload-server", + "version": "1.0.0", + "main": "upload-server.js", + "scripts": { + "start": "node upload-server.js" + }, + "dependencies": { + "express": "^4.18.2", + "multer": "^1.4.5-lts.1", + "uuid": "^9.0.0" + } +} \ No newline at end of file diff --git a/scripts/upload-server.js b/scripts/upload-server.js new file mode 100644 index 0000000..57bb308 --- /dev/null +++ b/scripts/upload-server.js @@ -0,0 +1,76 @@ +const express = require('express'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); +const { v4: uuidv4 } = require('uuid'); + +const app = express(); +const PORT = process.env.PORT || 3000; +const BASE_URL = process.env.BASE_URL || 'http://localhost:3000'; +const UPLOAD_DIR = process.env.UPLOAD_DIR || './uploads'; +const HASH_FILENAME = process.env.HASH_FILENAME === 'true'; + +if (!fs.existsSync(UPLOAD_DIR)) { + fs.mkdirSync(UPLOAD_DIR, { recursive: true }); +} + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, UPLOAD_DIR); + }, + filename: (req, file, cb) => { + if (HASH_FILENAME) { + const ext = path.extname(file.originalname); + const filename = uuidv4() + ext; + cb(null, filename); + } else { + cb(null, file.originalname); + } + } +}); + +const upload = multer({ storage }); + +app.post('/upload', upload.single('file'), (req, res) => { + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + + const fileUrl = `${BASE_URL}/files/${req.file.filename}`; + res.json({ url: fileUrl }); +}); + +app.put('/files/:path(*)', upload.single('file'), (req, res) => { + const filePath = req.params.path; + + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + + const oldPath = req.file.path; + const newPath = path.join(UPLOAD_DIR, filePath); + + const dir = path.dirname(newPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + fs.renameSync(oldPath, newPath); + + const fileUrl = `${BASE_URL}/files/${filePath}`; + res.json({ url: fileUrl }); +}); + +app.get('/files/:path(*)', (req, res) => { + const filePath = path.join(UPLOAD_DIR, req.params.path); + + if (!fs.existsSync(filePath)) { + return res.status(404).json({ error: 'File not found' }); + } + + res.sendFile(path.resolve(filePath)); +}); + +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); \ No newline at end of file