From fbcd1f22d63f6bac83fc3a87481e76808552972d Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 17 Jul 2021 03:10:28 +0200 Subject: Implement basic upload and listing --- assets/cargohold.css | 12 +++++++ assets/cargohold.js | 27 +++++++++++++-- backend/config.py | 4 +++ backend/main.py | 92 ++++++++++++++++++++++++++++++++++++++++++--------- config/uwsgi.ini | 4 +-- interface/listing.htm | 15 ++------- 6 files changed, 123 insertions(+), 31 deletions(-) diff --git a/assets/cargohold.css b/assets/cargohold.css index f3db7e9..bec6b3a 100644 --- a/assets/cargohold.css +++ b/assets/cargohold.css @@ -9,6 +9,18 @@ html { background-color: #425; } +.listing-entry, .listing-entry:visited { + display: block; + text-decoration: none; + color: #ccd; + background-color: #aaea; + padding: 0.5em; + font-size: 120%; + margin-top: 1em; + margin-bottom: 1em; + border-radius: 0.5em; +} + .queue-entry.errored { background-color: red; } diff --git a/assets/cargohold.js b/assets/cargohold.js index e1278b7..cd6cc34 100644 --- a/assets/cargohold.js +++ b/assets/cargohold.js @@ -41,7 +41,7 @@ function queue_work(){ }; req.onabort = function(evt){ console.log("Upload for " + item.file.name + " aborted"); - item.node.className += "errored"; + item.node.className += " errored"; }; req.onreadystatechange = function(evt){ console.log("Upload for " + item.file.name + " state " + req.readyState); @@ -96,10 +96,33 @@ function upload_start(element){ queue_run(); } +function listing_clear(){ + element("dirlisting").innerHTML = ""; +} + +function listing_add(name){ + let link = node("a", "listing-entry", name); + link.href = "file/" + encodeURIComponent(name); + element("dirlisting").appendChild(link); +} + +function listing_update(){ + listing_clear(); + let req = new XMLHttpRequest(); + req.onload = function(evt){ + let data = JSON.parse(req.response); + for(let i = 0; i < data.length; i++){ + listing_add(data[i]); + } + }; + req.open("GET", "listing"); + req.send(); +} + function init(){ element("file-submit").style.display = "none"; element("files").onchange = upload_start; - //setInterval(queue_run, 1000); + listing_update(); } window.onload = init; diff --git a/backend/config.py b/backend/config.py index 6cfab69..3f70c39 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1 +1,5 @@ homepage = "https://stumpf.es/" +fileroot = "/media/disk1/files.stumpf.es/" +global_limit = 0 +user_subdirs = True +database = "cargohold.db3" diff --git a/backend/main.py b/backend/main.py index ff703be..716ca13 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,33 +1,95 @@ -import HTTP import json import config +import cgi +import os +import sqlite3 +import mimetypes -def playout(filename): - return ["", [('Content-Type','text/html'), ("X-Accel-Redirect", filename)], None] +def playout(filename, content = "text/html"): + return ["", [('Content-Type', content if content else "application/binary"), ("X-Accel-Redirect", filename)], None] def home(): return ["", [('Content-Type','text/html'), ("Location", config.homepage)], "302 Home"] +def target_filename_internal(session, file): + target = "" + if not file: + file = "" + if config.user_subdirs: + target = session["user"] + "/" + return target + session["path"] + "/" + file + + +def target_filename(session, file): + return config.fileroot + target_filename_internal(session, file) + +def resolve_alias(alias): + session = None + db = sqlite3.connect(config.database, check_same_thread = False) + cursor = db.cursor() + cursor.execute("SELECT user, real, access, storage FROM aliases WHERE alias=:alias", {"alias": alias}) + data = cursor.fetchone() + if data: + session = {"user": data[0], "path": data[1], "access": data[2], "limit": data[3]} + else: + print("Unknown alias " + alias) + db.close() + return session + +def listing(session): + target = target_filename(session, None) + files = os.listdir(target) + return [json.dumps(files), [('Content-Type','application/json')], "200 OK"] + +def upload(session, post): + if post["file"].filename: + target = target_filename(session, os.path.basename(post["file"].filename)) + try: + open(target, 'wb').write(post["file"].file.read()) + print("Uploaded " + target) + return ["", [('Content-Type','text/html')], "200 OK"] + except PermissionError as e: + print("Failed to store " + target + ": " + e.msg) + return ["", [('Content-Type','text/html')], "500 Error"] + else: + print("Upload failed") + return ["", [('Content-Type','text/html')], "500 Error"] + def route(path, env, session, post): - if path[0] == "8cabc1fce52bcb565ae203267ce7e73f69a9272e": - return playout("interface/listing.htm") - if path[0] == "test": - return playout("interface/listing.htm") + # Get mapped user/path/limits + session = resolve_alias(path[0]) + + if not session: + return home() + + print(json.dumps(session)) + + if len(path) > 1 and path[1] == "upload": + return upload(session, post) + + if len(path) > 1 and path[1] == "listing": + return listing(session) + + if len(path) > 1 and path[1] == "file": + print("/data/" + target_filename_internal(session, path[2])) + return playout("/data/" + target_filename_internal(session, path[2]), mimetypes.guess_type(path[2])[0]) - # Default path - return home() + return playout("/interface/listing.htm") def handle_request(env, response): path = env.get('PATH_INFO', '').lstrip('/').split('/') - post = {} + post = None headers = [] + session = None # Read POST data try: - content_length = int(env.get('CONTENT_LENGTH', '0')) - post_raw = env["wsgi.input"].read(content_length).decode('utf-8') - if env.get('CONTENT_TYPE', '') == "multipart/form-data": - post = HTTP.formdata(post_raw) + if env.get('CONTENT_TYPE', '').startswith("multipart/form-data"): + post = cgi.FieldStorage(environ=env, fp=env['wsgi.input'], keep_blank_values=True) + else: + content_length = int(env.get('CONTENT_LENGTH', '0')) + post_raw = env["wsgi.input"].read(content_length).decode('utf-8') + post = post_raw except ValueError as e: post = None @@ -35,4 +97,4 @@ def handle_request(env, response): data, addtl_headers, code = route(path, env, session, post) headers.extend(addtl_headers) response(code if code else "200 OK", headers) - return [bytes(data)] + return [bytes(data, "utf-8")] diff --git a/config/uwsgi.ini b/config/uwsgi.ini index 6ea3e51..cdf22c5 100644 --- a/config/uwsgi.ini +++ b/config/uwsgi.ini @@ -2,10 +2,10 @@ processes = 4 master = 1 socket = /tmp/cargohold.sock -chdir = /var/www/cargohold/ +chdir = /var/www/cargohold/backend/ wsgi-file = main.py callable = handle_request uid = www-data gid = www-data plugin = python3 - +limit-post = 524288000 diff --git a/interface/listing.htm b/interface/listing.htm index 27b622a..220df68 100644 --- a/interface/listing.htm +++ b/interface/listing.htm @@ -4,9 +4,9 @@ cargohold - - - + + + - -- cgit v1.2.3