Add file upload server/container
This commit is contained in:
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@@ -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"]
|
||||||
@@ -94,38 +94,20 @@ services:
|
|||||||
- webapi
|
- webapi
|
||||||
- webui
|
- webui
|
||||||
- seq
|
- seq
|
||||||
|
- upload-server
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
|
|
||||||
zipline:
|
upload-server:
|
||||||
image: ghcr.io/diced/zipline:latest
|
build: .
|
||||||
expose:
|
ports:
|
||||||
- "3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
- CORE_RETURN_HTTPS=true
|
- BASE_URL=https://impr.ink
|
||||||
- CORE_SECRET=${ZIPLINE_SECRET}
|
- HASH_FILENAME=true
|
||||||
- CORE_HOST=0.0.0.0
|
- UPLOAD_DIR=/app/uploads
|
||||||
- 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}
|
|
||||||
volumes:
|
volumes:
|
||||||
- zipline_data:/zipline/uploads
|
- ./uploads:/app/uploads
|
||||||
- zipline_public:/zipline/public
|
|
||||||
- zipline_db:/zipline
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
|
|
||||||
@@ -134,6 +116,4 @@ networks:
|
|||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
zipline_data:
|
upload_data:
|
||||||
zipline_public:
|
|
||||||
zipline_db:
|
|
||||||
@@ -21,8 +21,8 @@ http {
|
|||||||
server seq:80;
|
server seq:80;
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream zipline {
|
upstream upload-server {
|
||||||
server zipline:3000;
|
server upload-server:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -85,23 +85,38 @@ http {
|
|||||||
rewrite ^/seq/(.*)$ /$1 break;
|
rewrite ^/seq/(.*)$ /$1 break;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /res {
|
location /upload {
|
||||||
proxy_pass http://zipline;
|
proxy_pass http://upload-server/upload;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
proxy_set_header X-Forwarded-Prefix /res;
|
|
||||||
|
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_read_timeout 300s;
|
proxy_read_timeout 300s;
|
||||||
proxy_send_timeout 300s;
|
proxy_send_timeout 300s;
|
||||||
|
|
||||||
rewrite ^/res/(.*)$ /$1 break;
|
client_max_body_size 1G;
|
||||||
rewrite ^/res$ / break;
|
}
|
||||||
|
|
||||||
|
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 / {
|
location / {
|
||||||
|
|||||||
13
scripts/package.json
Normal file
13
scripts/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
76
scripts/upload-server.js
Normal file
76
scripts/upload-server.js
Normal file
@@ -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}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user