Initial commit
This commit is contained in:
commit
3e72ecab20
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.history
|
||||
.venv
|
||||
src/upload/__pycache__
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
all: ensure_env build serve
|
||||
|
||||
ensure_env:
|
||||
-@python3 -m venv .venv
|
||||
|
||||
build:
|
||||
./.venv/bin/python -m pip install build
|
||||
./.venv/bin/python -m build .
|
||||
./.venv/bin/python -m pip install -e .
|
||||
|
||||
serve:
|
||||
./.venv/bin/rupload.serve
|
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# RUpload
|
||||
|
||||
## About
|
||||
Easiest way to upload files to a server.
|
||||
|
||||
Since it doesn't have captcha or whatsoever, it's only usable with people you trust on a not listed domain. It's destined for public use, as long the domain isn't listed in a search engine. It has a limitation for file size, so it would never go terribly wrong.
|
||||
|
||||
## Installation
|
||||
```
|
||||
python3 -m venv .venv
|
||||
./venv/bin/python -m pip install .
|
||||
```
|
||||
|
||||
## Usage
|
||||
```
|
||||
rupload.serve [host(127.0.0.1)] [port] [destination path for uploads] [max file size in bytes]
|
||||
```
|
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
25
setup.cfg
Normal file
25
setup.cfg
Normal file
@ -0,0 +1,25 @@
|
||||
[metadata]
|
||||
name = rupload
|
||||
version = 1.3.37
|
||||
description = upload tool
|
||||
author = Retoor
|
||||
author_email = retoor@molodetz.nl
|
||||
license = MIT
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
package_dir =
|
||||
= src
|
||||
python_requires = >=3.7
|
||||
install_requires =
|
||||
aiohttp==3.10.10
|
||||
dataset==1.6.2
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
rupload.serve = rupload.cli:main
|
29
src/rupload.egg-info/PKG-INFO
Normal file
29
src/rupload.egg-info/PKG-INFO
Normal file
@ -0,0 +1,29 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: rupload
|
||||
Version: 1.3.37
|
||||
Summary: upload tool
|
||||
Author: Retoor
|
||||
Author-email: retoor@molodetz.nl
|
||||
License: MIT
|
||||
Requires-Python: >=3.7
|
||||
Description-Content-Type: text/markdown
|
||||
Requires-Dist: aiohttp==3.10.10
|
||||
Requires-Dist: dataset==1.6.2
|
||||
|
||||
# RUpload
|
||||
|
||||
## About
|
||||
Easiest way to upload files to a server.
|
||||
|
||||
Since it doesn't have captcha or whatsoever, it's only usable with people you trust on a not listed domain. It's destined for public use, as long the domain isn't listed in a search engine. It has a limitation for file size, so it would never go terribly wrong.
|
||||
|
||||
## Installation
|
||||
```
|
||||
python3 -m venv .venv
|
||||
./venv/bin/python -m pip install .
|
||||
```
|
||||
|
||||
## Usage
|
||||
```
|
||||
rupload.serve [host(127.0.0.1)] [port] [destination path for uploads] [max file size in bytes]
|
||||
```
|
13
src/rupload.egg-info/SOURCES.txt
Normal file
13
src/rupload.egg-info/SOURCES.txt
Normal file
@ -0,0 +1,13 @@
|
||||
README.md
|
||||
pyproject.toml
|
||||
setup.cfg
|
||||
src/rupload/__init__.py
|
||||
src/rupload/__main__.py
|
||||
src/rupload/app.py
|
||||
src/rupload/cli.py
|
||||
src/rupload.egg-info/PKG-INFO
|
||||
src/rupload.egg-info/SOURCES.txt
|
||||
src/rupload.egg-info/dependency_links.txt
|
||||
src/rupload.egg-info/entry_points.txt
|
||||
src/rupload.egg-info/requires.txt
|
||||
src/rupload.egg-info/top_level.txt
|
1
src/rupload.egg-info/dependency_links.txt
Normal file
1
src/rupload.egg-info/dependency_links.txt
Normal file
@ -0,0 +1 @@
|
||||
|
2
src/rupload.egg-info/entry_points.txt
Normal file
2
src/rupload.egg-info/entry_points.txt
Normal file
@ -0,0 +1,2 @@
|
||||
[console_scripts]
|
||||
rupload.serve = rupload.cli:main
|
2
src/rupload.egg-info/requires.txt
Normal file
2
src/rupload.egg-info/requires.txt
Normal file
@ -0,0 +1,2 @@
|
||||
aiohttp==3.10.10
|
||||
dataset==1.6.2
|
1
src/rupload.egg-info/top_level.txt
Normal file
1
src/rupload.egg-info/top_level.txt
Normal file
@ -0,0 +1 @@
|
||||
rupload
|
0
src/rupload/__init__.py
Normal file
0
src/rupload/__init__.py
Normal file
0
src/rupload/__main__.py
Normal file
0
src/rupload/__main__.py
Normal file
BIN
src/rupload/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
src/rupload/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/rupload/__pycache__/app.cpython-312.pyc
Normal file
BIN
src/rupload/__pycache__/app.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/rupload/__pycache__/cli.cpython-312.pyc
Normal file
BIN
src/rupload/__pycache__/cli.cpython-312.pyc
Normal file
Binary file not shown.
151
src/rupload/app.py
Normal file
151
src/rupload/app.py
Normal file
@ -0,0 +1,151 @@
|
||||
import aiohttp
|
||||
from aiohttp import web
|
||||
import asyncio
|
||||
import pathlib
|
||||
|
||||
|
||||
class Rupload(web.Application):
|
||||
def __init__(self, upload_path, max_file_size):
|
||||
self.upload_path = upload_path
|
||||
self.max_file_size = max_file_size
|
||||
super().__init__()
|
||||
|
||||
|
||||
UPLOAD_FOLDER = "uploads"
|
||||
pathlib.Path(UPLOAD_FOLDER).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
UPLOAD_PAGE = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>File Upload</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f9;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
padding: 10px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 20px;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
font-size: 14px;
|
||||
color: #777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Upload Your File</h1>
|
||||
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||
<label for="file">Choose a file to upload:</label>
|
||||
<input type="file" name="file" id="file" required>
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
async def handle_upload(request):
|
||||
reader = await request.multipart()
|
||||
field = await reader.next()
|
||||
|
||||
if field.name == "file":
|
||||
|
||||
filename = field.filename
|
||||
print(filename)
|
||||
if ".." or "/" in filename:
|
||||
return web.Response(status=400, text="Invalid filename.")
|
||||
|
||||
filepath = pathlib.Path(UPLOAD_FOLDER).joinpath(filename)
|
||||
|
||||
if filepath.exists():
|
||||
return web.Response(status=400, text="File already exists.")
|
||||
|
||||
with filepath.open("wb") as f:
|
||||
file_size = 0
|
||||
while True:
|
||||
chunk = await field.read_chunk()
|
||||
if not chunk:
|
||||
break
|
||||
file_size += len(chunk)
|
||||
if file_size > max_file_size:
|
||||
f.close()
|
||||
f.unlink()
|
||||
return web.Response(
|
||||
status=413,
|
||||
text="File is too large. Maximum file size is {} bytes.".format(
|
||||
max_file_size
|
||||
),
|
||||
)
|
||||
f.write(chunk)
|
||||
|
||||
return web.Response(text=f"File {filename} uploaded successfully!")
|
||||
return web.Response(status=400, text="No file uploaded.")
|
||||
|
||||
|
||||
async def handle_index(request):
|
||||
return web.Response(text=UPLOAD_PAGE, content_type="text/html")
|
||||
|
||||
|
||||
def create_app(upload_path="upload", max_file_size=1024 * 1024 * 50):
|
||||
app = Rupload(upload_path=upload_path, max_file_size=max_file_size)
|
||||
app.add_routes([web.get("/", handle_index), web.post("/upload", handle_upload)])
|
||||
return app
|
36
src/rupload/cli.py
Normal file
36
src/rupload/cli.py
Normal file
@ -0,0 +1,36 @@
|
||||
import argparse
|
||||
from aiohttp import web
|
||||
from rupload.app import create_app
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Start the file upload server.")
|
||||
parser.add_argument(
|
||||
"--hostname", type=str, default="127.0.0.1", help="The hostname for the server."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", type=int, default=8081, help="The port to bind the server to."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--upload_folder",
|
||||
type=str,
|
||||
default="uploads",
|
||||
help="Directory to store uploaded files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_file_size",
|
||||
type=int,
|
||||
default=50 * 1024 * 1024,
|
||||
help="Maximum file size in bytes (default is 50MB).",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
app = create_app(upload_path=args.upload_folder, max_file_size=args.max_file_size)
|
||||
web.run_app(app, host=args.hostname, port=args.port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user