Initial Commit

This commit is contained in:
abiteman
2025-01-22 13:10:59 -06:00
parent 5ba5d87118
commit f2927d97db
8 changed files with 1357 additions and 0 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
node_modules
npm-debug.log
uploads/*
.env
.git
.gitignore
README.md

2
.gitignore vendored
View File

@@ -128,3 +128,5 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
uploads/

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN mkdir -p uploads
EXPOSE 3000
CMD ["node", "server.js"]

1030
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "dumbdrop",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "A simple file upload application",
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1"
}
}

128
public/index.html Normal file
View File

@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dumb Drop - Simple File Upload</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>Dumb Drop</h1>
<div class="upload-container" id="dropZone">
<div class="upload-content">
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
<p>Drag and drop files here<br>or</p>
<input type="file" id="fileInput" multiple hidden>
<button onclick="document.getElementById('fileInput').click()">Browse Files</button>
</div>
</div>
<div id="fileList" class="file-list"></div>
<button id="uploadButton" class="upload-button" style="display: none;">Upload Files</button>
</div>
<script>
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const fileList = document.getElementById('fileList');
const uploadButton = document.getElementById('uploadButton');
let files = [];
// Prevent default drag behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
// Highlight drop zone when dragging over it
['dragenter', 'dragover'].forEach(eventName => {
dropZone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropZone.addEventListener(eventName, unhighlight, false);
});
// Handle dropped files
dropZone.addEventListener('drop', handleDrop, false);
fileInput.addEventListener('change', handleFiles, false);
uploadButton.addEventListener('click', uploadFiles);
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
function highlight(e) {
dropZone.classList.add('highlight');
}
function unhighlight(e) {
dropZone.classList.remove('highlight');
}
function handleDrop(e) {
files = [...e.dataTransfer.files];
updateFileList();
}
function handleFiles(e) {
files = [...e.target.files];
updateFileList();
}
function updateFileList() {
fileList.innerHTML = '';
files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.textContent = `${file.name} (${formatFileSize(file.size)})`;
fileList.appendChild(fileItem);
});
uploadButton.style.display = files.length > 0 ? 'block' : 'none';
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
async function uploadFiles() {
const formData = new FormData();
files.forEach(file => {
formData.append('files', file);
});
try {
uploadButton.disabled = true;
uploadButton.textContent = 'Uploading...';
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
alert(result.message);
// Reset the form
files = [];
updateFileList();
uploadButton.textContent = 'Upload Files';
} catch (error) {
alert('Error uploading files');
console.error('Error:', error);
} finally {
uploadButton.disabled = false;
}
}
</script>
</body>
</html>

118
public/styles.css Normal file
View File

@@ -0,0 +1,118 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
body {
min-height: 100vh;
background: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 100%;
max-width: 600px;
padding: 20px;
text-align: center;
}
h1 {
color: #333;
margin-bottom: 30px;
font-size: 2.5rem;
font-weight: 700;
}
.upload-container {
background: white;
border-radius: 10px;
padding: 40px 20px;
border: 2px dashed #ccc;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 20px;
}
.upload-container.highlight {
border-color: #4CAF50;
background: #f0f9f0;
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.upload-content svg {
color: #666;
margin-bottom: 10px;
}
.upload-content p {
color: #666;
font-size: 1.1rem;
line-height: 1.5;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: #45a049;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.file-list {
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.file-item {
background: white;
padding: 10px 15px;
border-radius: 5px;
text-align: left;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.upload-button {
margin-top: 20px;
width: 100%;
max-width: 200px;
margin-left: auto;
margin-right: auto;
}
@media (max-width: 480px) {
.container {
padding: 10px;
}
h1 {
font-size: 2rem;
}
.upload-container {
padding: 20px 10px;
}
}

38
server.js Normal file
View File

@@ -0,0 +1,38 @@
const express = require('express');
const multer = require('multer');
const path = require('path');
const cors = require('cors');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 3000;
const uploadDir = process.env.UPLOAD_DIR || 'uploads';
// Configure multer for file upload
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`);
}
});
const upload = multer({ storage: storage });
// Middleware
app.use(cors());
app.use(express.static('public'));
// Routes
app.post('/upload', upload.array('files'), (req, res) => {
if (!req.files || req.files.length === 0) {
return res.status(400).json({ message: 'No files uploaded' });
}
res.json({ message: 'Files uploaded successfully' });
});
// Start server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});