Publish #1

Merged
IgorVolochay merged 29 commits from dev into main 2026-01-10 16:02:13 +00:00
5 changed files with 273 additions and 0 deletions
Showing only changes of commit 1c6ab276c8 - Show all commits
+49
View File
@@ -0,0 +1,49 @@
from fastapi import FastAPI, File, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from typing import Annotated
import dotenv
import os
import asyncio
import uvicorn
dotenv.load_dotenv()
disable_docs = os.getenv("DISABLE_DOCS", "true").lower() == "true"
app: FastAPI = FastAPI(title="This OR That",
summary="OpenAPI schema for \"This OR That\" project!",
version="0.1",
contact={"GitHub": "https://github.com/IgorVolochay/thisORthat"},
docs_url=None if disable_docs else "/docs",
redoc_url=None if disable_docs else "/redoc",
openapi_url=None if disable_docs else "/openapi.json")
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/")
def root():
return "Hello!"
@app.post("/upload_file")
@app.post("/upload_file")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}
async def main():
config = uvicorn.Config("main:app", port=5000, log_level="debug")
server = uvicorn.Server(config)
await server.serve()
if __name__ == "__main__":
asyncio.run(main())
+12
View File
@@ -0,0 +1,12 @@
[project]
name = "drop-me-files-analog"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"dotenv>=0.9.9",
"fastapi>=0.121.1",
"python-multipart>=0.0.20",
"uvicorn>=0.38.0",
]
+25
View File
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Аналог DropMeFiles</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<h1>Аналог DropMeFiles</h1>
<div class="upload-container">
<form id="uploadForm">
<label class="dropzone" id="dropzone">
<input id="fileInput" type="file" multiple>
<p class="strong">Перетащите файлы сюда или нажмите, чтобы выбрать</p>
<p>Файлы будут отправлены на сервер после нажатия кнопки</p>
</label>
<button type="submit" id="uploadButton" disabled>Загрузить</button>
</form>
<div class="status" id="status"></div>
</div>
<script src="./script.js"></script>
</body>
</html>
+77
View File
@@ -0,0 +1,77 @@
const fileInput = document.getElementById('fileInput');
const dropzone = document.getElementById('dropzone');
const uploadButton = document.getElementById('uploadButton');
const uploadForm = document.getElementById('uploadForm');
const statusDiv = document.getElementById('status');
const updateButtonState = () => {
uploadButton.disabled = !fileInput.files || fileInput.files.length === 0;
};
const setStatus = (message, type = '') => {
statusDiv.textContent = message;
statusDiv.className = type ? `status ${type}` : 'status';
};
dropzone.addEventListener('dragover', (event) => {
event.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', () => {
dropzone.classList.remove('dragover');
});
dropzone.addEventListener('drop', (event) => {
event.preventDefault();
dropzone.classList.remove('dragover');
if (event.dataTransfer?.files?.length) {
fileInput.files = event.dataTransfer.files;
updateButtonState();
}
});
fileInput.addEventListener('change', () => {
updateButtonState();
});
uploadForm.addEventListener('submit', async (event) => {
event.preventDefault();
if (!fileInput.files || fileInput.files.length === 0) {
setStatus('Выберите файлов для загрузки.', 'error');
return;
}
const formData = new FormData();
Array.from(fileInput.files).forEach((file) => {
formData.append('files', file);
});
uploadButton.disabled = true;
dropzone.classList.remove('dragover');
setStatus('Загрузка...', '');
try {
const response = await fetch('http://192.168.0.132:5000/upload_file', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`Ошибка сервера: ${response.status}`);
}
const data = await response.text();
setStatus(`Путь к файлу: ${data}`, 'success');
fileInput.value = '';
} catch (error) {
setStatus(`Не удалось загрузить файлы: ${error.message}`, 'error');
} finally {
uploadButton.disabled = false;
}
});
updateButtonState();
+110
View File
@@ -0,0 +1,110 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
background: radial-gradient(circle at top, #f7f7fb, #eff1f8);
color: #111827;
padding: 24px;
}
h1 {
font-size: clamp(32px, 4vw, 48px);
margin-bottom: 32px;
text-align: center;
}
.upload-container {
width: min(640px, 100%);
background: #ffffffdd;
border-radius: 20px;
box-shadow: 0 20px 45px -20px rgba(15, 23, 42, 0.65);
padding: 32px;
display: flex;
flex-direction: column;
gap: 24px;
backdrop-filter: blur(8px);
}
.dropzone {
border: 2px dashed #6366f1;
border-radius: 16px;
padding: 48px 24px;
text-align: center;
transition: all 0.2s ease;
background: rgba(99, 102, 241, 0.04);
cursor: pointer;
position: relative;
}
.dropzone.dragover {
background: rgba(99, 102, 241, 0.12);
transform: translateY(-2px);
box-shadow: 0 14px 28px -24px rgba(99, 102, 241, 0.65);
}
.dropzone input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}
.dropzone p {
margin: 8px 0;
color: #4b5563;
font-size: 16px;
}
.dropzone p.strong {
font-weight: 600;
color: #1f2937;
font-size: 18px;
}
button {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
border: none;
border-radius: 12px;
padding: 14px 20px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
button:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 16px 32px -24px rgba(99, 102, 241, 0.8);
}
button:disabled {
background: #cbd5f5;
cursor: not-allowed;
box-shadow: none;
}
.status {
min-height: 24px;
font-size: 15px;
color: #374151;
line-height: 1.5;
word-break: break-all;
}
.status.error {
color: #dc2626;
}
.status.success {
color: #166534;
}