Compare commits

..
6 Commits
Author SHA1 Message Date
retoor 5f518c8df7 Added upload directory contents to .gitignore.
RUpload build / Build (push) Successful in 1m14s
2024-11-26 06:05:24 +01:00
retoor 15869d6537 Removed uploads content 2024-11-26 06:04:50 +01:00
retoor 3bd46cb640 Created build server configuration. 2024-11-26 06:04:33 +01:00
retoor 1ed30e0d80 Updated readme. 2024-11-26 05:57:50 +01:00
retoor 67d91a42e5 Updated readme. 2024-11-26 05:57:12 +01:00
retoor 3e72ecab20 Initial commit 2024-11-26 04:34:46 +01:00
12 changed files with 55 additions and 167 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
.history
.venv
src/rupload/__pycache__
src/upload/__pycache__
uploads/*
+7 -6
View File
@@ -1,14 +1,15 @@
# RUpload
Easiest file manager for your server made for public use.
## About
Easiest way to upload files to a server.
It doesn't have captcha or whatsoever. It's designed for public use, as long the domain isn't listed in a search engine. It has a limitation for file size, that's it's only security. Rate limiting is in consideration. The default file size is 50Mb. The default storage quota for whole directory is 10Gb. Default config will prevent your server for heavy abuse.
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
1. `make ensure_venv`
2. `make install`
3. `source ./.venv/bin/activate`
4. `rupload.serve` (See parameters below. Running with default config using no parameters works too.)
```
python3 -m venv .venv
./venv/bin/python -m pip install .
```
## Usage
```bash
Binary file not shown.
BIN
View File
Binary file not shown.
-1
View File
@@ -16,7 +16,6 @@ python_requires = >=3.7
install_requires =
aiohttp==3.10.10
dataset==1.6.2
pillow
[options.packages.find]
where = src
+8 -8
View File
@@ -1,4 +1,4 @@
Metadata-Version: 2.4
Metadata-Version: 2.1
Name: rupload
Version: 1.3.37
Summary: upload tool
@@ -9,19 +9,19 @@ Requires-Python: >=3.7
Description-Content-Type: text/markdown
Requires-Dist: aiohttp==3.10.10
Requires-Dist: dataset==1.6.2
Requires-Dist: pillow
# RUpload
Easiest file manager for your server made for public use.
## About
Easiest way to upload files to a server.
It doesn't have captcha or whatsoever. It's designed for public use, as long the domain isn't listed in a search engine. It has a limitation for file size, that's it's only security. Rate limiting is in consideration. The default file size is 50Mb. The default storage quota for whole directory is 10Gb. Default config will prevent your server for heavy abuse.
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
1. `make ensure_venv`
2. `make install`
3. `source ./.venv/bin/activate`
4. `rupload.serve` (See parameters below. Running with default config using no parameters works too.)
```
python3 -m venv .venv
./venv/bin/python -m pip install .
```
## Usage
```bash
-1
View File
@@ -1,3 +1,2 @@
aiohttp==3.10.10
dataset==1.6.2
pillow
Binary file not shown.
Binary file not shown.
Binary file not shown.
+37 -135
View File
@@ -1,56 +1,33 @@
import pathlib
import aiohttp
from aiohttp import web
MAX_FILE_SIZE = 1024 * 1024 * 300 # 50Mb
UPLOAD_FOLDER_QUOTA = 10 * 1024 * 1024 * 1024 # 10Gb
UPLOAD_URL = "/"
UPLOAD_PATH = "uploads"
import asyncio
import pathlib
class Rupload(web.Application):
def __init__(
self,
upload_url: str = UPLOAD_URL,
upload_path: str = UPLOAD_PATH,
max_file_size: int = MAX_FILE_SIZE,
upload_folder_quota: int = UPLOAD_FOLDER_QUOTA,
):
self.upload_path = upload_path.rstrip("/")
def __init__(self, upload_url:str="/uploads/", upload_path:str="uploads", max_file_size:int=1024*1024*50):
self.upload_path = upload_path
self.max_file_size = max_file_size
self.upload_url = upload_url.rstrip("/")
self.upload_folder_quota = upload_folder_quota
self.upload_url = upload_url
super().__init__()
@property
def is_upload_folder_quota_reached(self):
return pathlib.Path(self.upload_path).stat().st_size > self.upload_folder_quota
def generate_upload_url(self, filename):
return self.upload_url + "/" + filename
UPLOAD_PAGE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=0.9">
<title>RUpload 1.33.7</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {
font-family: "Courier New", Courier, monospace;
background-color: #111111;
body {
font-family: Arial, sans-serif;
background-color: #f4f4f9;
display: flex;
justify-content: center;
align-items: center;
word-break: break-all;
margin: 0;
filter: invert(1);
}
a {
text-decoration: none;
}
.container {
@@ -61,10 +38,7 @@ UPLOAD_PAGE = """
width: 100%;
max-width: 500px;
}
.container-images {
filter: invert(1);
}
h1 {
font-size: 24px;
margin-bottom: 20px;
@@ -73,7 +47,6 @@ UPLOAD_PAGE = """
h2 {
font-size: 20px;
margin-top: 20px;
margin-bottom: 20px;
color: #333;
clear: both;
@@ -123,9 +96,8 @@ UPLOAD_PAGE = """
width: 100px;
height: 100px;
object-fit: cover;
margin: 3px;
margin: 10px;
float:left;
border-radius: 5px;
}
</style>
</head>
@@ -140,9 +112,9 @@ UPLOAD_PAGE = """
<button type="submit">Upload</button>
</form>
<br />
Click <a href="#files">here</a> to see <a href="#files">uploaded files</a> (non-image) at the bottom of this page.
Click <a href="#files">here</a> to see <a href="#files">uploaded files</a> (non-image) at the bottom of this page.<br />
<h2>Uploaded images:</h2>
<div class="container-images">
<div class="container-image">
[images_html]
</div>
<h2 id="files">Uploaded files:</h2>
@@ -157,7 +129,6 @@ UPLOAD_PAGE = """
</html>
"""
def format_size(size):
if size < 1024:
return f"{size} B"
@@ -170,23 +141,13 @@ def format_size(size):
else:
return f"{size / 1024 * 1024 * 1024 * 1024:.2f} TB"
def get_images(path):
images = []
for image in pathlib.Path(path).iterdir():
if image.is_file() and image.suffix.lower() in [
".png",
".jpg",
".gif",
".jpeg",
".bmp",
".webp",
".svg",
]:
if image.is_file() and image.suffix in [".png", ".jpg", ".gif", ".jpeg", ".bmp"]:
images.append(image)
return images
def get_files(path):
images = get_images(path)
files = []
@@ -195,30 +156,21 @@ def get_files(path):
files.append(file)
return files
def create_images_html(url, image_paths):
images_html = ""
for image_path in image_paths:
path = url.rstrip("/") + "/" + image_path.name
thumbnail_path = "/thumbnail/" + url.strip("/") + "/" + image_path.name
images_html += f'<a href="{path}"><img class="thumbnail" src="{thumbnail_path}" alt="{image_path.name}" /></a>\n'
images_html += f'<img class="thumbnail" src="{path}" alt="{image_path.name}">'
return images_html
def create_files_html(url, file_paths):
files_html = ""
for file_path in file_paths:
path = url.rstrip("/") + "/" + file_path.name
files_html += f'<a href="{path}">{file_path.name}</a> ({format_size(file_path.stat().st_size)})\n<br>\n'
files_html += f'<a href="{path}">{file_path.name}</a> ({format_size(file_path.stat().st_size)})<br>'
return files_html
async def handle_upload(request: web.Request):
if request.app.is_upload_folder_quota_reached:
return web.HTTPFound(
"/?message=Server%20reached%20quota!%20Contact%20administrator."
)
async def handle_upload(request:web.Request):
reader = await request.multipart()
field = await reader.next()
app = request.app
@@ -254,88 +206,38 @@ async def handle_upload(request: web.Request):
print(f"File {filename} deleted.")
return web.Response(
status=413,
text=f"File is too large. Maximum file size is {app.max_file_size} bytes.",
text="File is too large. Maximum file size is {} bytes.".format(
app.max_file_size
),
)
f.write(chunk)
print(f"File {filename} uploaded successfully.")
uploaded_url = request.app.generate_upload_url(filename)
uploaded_url = app.upload_url.rstrip("/") + "/" + filename
print(f"File {filename} is now available at: {uploaded_url}.")
return web.HTTPFound(
"/?message=File is succesfully uploaded and is available here: "
+ uploaded_url
)
return web.HTTPFound("/?message=File is succesfully uploaded and is available here:" + uploaded_url)
print("No file uploaded.")
return web.Response(status=400, text="No file uploaded.")
async def handle_thumbnail(request: web.Request):
path = request.match_info["path"]
safe_path = pathlib.Path(request.app.upload_path).joinpath(pathlib.Path(path).name)
if not safe_path.is_file():
return web.Response(status=400, text="Invalid file type.")
thumbnail_path = pathlib.Path(request.app.upload_path).joinpath(".thumbnail").joinpath(pathlib.Path(safe_path).name)
if not thumbnail_path.parent.exists():
thumbnail_path.parent.mkdir(exist_ok=True,parents=False)
if not thumbnail_path.exists():
from PIL import Image
image = Image.open(safe_path)
image.thumbnail((200, 200))
image.save(thumbnail_path)
return web.FileResponse(thumbnail_path)
async def handle_index(request: web.Request):
async def handle_index(request:web.Request):
image_paths = get_images(request.app.upload_path)
images_html = create_images_html(
url=request.app.upload_url, image_paths=image_paths
)
images_html = create_images_html(url=request.app.upload_url,image_paths=image_paths)
file_paths = get_files(request.app.upload_path)
files_html = create_files_html(url=request.app.upload_url, file_paths=file_paths)
html_content = UPLOAD_PAGE.replace("[images_html]", images_html)
html_content = html_content.replace("[files_html]", files_html)
files_html = create_files_html(url=request.app.upload_url,file_paths=file_paths)
html_content = UPLOAD_PAGE.replace("[images_html]",images_html)
html_content = html_content.replace("[files_html]",files_html)
message = request.query.get("message", "")
if request.app.is_upload_folder_quota_reached:
message = "Server reached quota! Contact administrator."
if "is available here: " in message:
url = message[message.find("here: ") + len("here: ") :]
message = message[: message.find("here: ") + len("here: ")]
message += f'<a href="{url}">'
message += f"{url}</a>"
if not message:
message = (
"Any file type is allowed thus also binary/executable or source files."
)
if request.app.upload_url in message:
url = message[message.find(request.app.upload_url):]
message = message.replace(request.app.upload_url, f" <a href=\"{url}")
message += f"\">{url}</a>"
if message:
message += "<br /><br />"
html_content = html_content.replace("[message]", message)
html_content = html_content.replace("[message]",message)
return web.Response(text=html_content, content_type="text/html")
def create_app(
upload_url: str = UPLOAD_URL,
upload_path: str = UPLOAD_PATH,
max_file_size: int = MAX_FILE_SIZE,
upload_folder_quota: int = UPLOAD_FOLDER_QUOTA,
):
app = Rupload(
upload_url=upload_url,
upload_path=upload_path,
max_file_size=max_file_size,
upload_folder_quota=upload_folder_quota,
)
pathlib.Path(upload_path).mkdir(parents=True, exist_ok=True)
app.add_routes(
[
web.get("/", handle_index),
web.post("/upload", handle_upload),
web.static("/", upload_path),
web.get("/thumbnail/{path:.*}", handle_thumbnail),
web.static("/uploads", upload_path),
]
)
def create_app(upload_url:str="/", upload_path:str="upload", max_file_size:int=1024 * 1024 * 50):
app = Rupload(upload_url=upload_url, upload_path=upload_path, max_file_size=max_file_size)
app.add_routes([web.get("/", handle_index), web.post("/upload", handle_upload),web.static("/uploads", "uploads")])
return app
+2 -15
View File
@@ -1,7 +1,5 @@
import argparse
from aiohttp import web
from aiohttp import web
from rupload.app import create_app
@@ -31,23 +29,12 @@ def parse_args():
default=50 * 1024 * 1024,
help="Maximum file size in bytes (default is 50MB).",
)
parser.add_argument(
"--upload_folder_quota",
type=int,
default=10 * 1024 * 1024 * 1024,
help="Quata upload folder in bytes (default is 10GB).",
)
return parser.parse_args()
def main():
args = parse_args()
app = create_app(
upload_url=args.upload_url,
upload_path=args.upload_folder,
max_file_size=args.max_file_size,
upload_folder_quota=args.upload_folder_quota,
)
app = create_app(upload_url=args.upload_url, upload_path=args.upload_folder, max_file_size=args.max_file_size)
web.run_app(app, host=args.hostname, port=args.port)