aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2021-07-17 03:10:28 +0200
committercbdev <cb@cbcdn.com>2021-07-17 03:10:28 +0200
commitfbcd1f22d63f6bac83fc3a87481e76808552972d (patch)
tree85d7895f9246eba1a473667e573bf55379e461b9
parent2fcb061a09cb237354ca6a5b7a0309745d3e4caf (diff)
downloadcargohold-fbcd1f22d63f6bac83fc3a87481e76808552972d.tar.gz
cargohold-fbcd1f22d63f6bac83fc3a87481e76808552972d.tar.bz2
cargohold-fbcd1f22d63f6bac83fc3a87481e76808552972d.zip
Implement basic upload and listing
-rw-r--r--assets/cargohold.css12
-rw-r--r--assets/cargohold.js27
-rw-r--r--backend/config.py4
-rw-r--r--backend/main.py92
-rw-r--r--config/uwsgi.ini4
-rw-r--r--interface/listing.htm15
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 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>cargohold</title>
- <link rel="stylesheet" href="assets/cargohold.css" />
- <link rel="icon" href="assets/cargohold.ico" />
- <script src="assets/cargohold.js"></script>
+ <link rel="stylesheet" href="../assets/cargohold.css" />
+ <link rel="icon" href="../assets/cargohold.ico" />
+ <script src="../assets/cargohold.js"></script>
</head>
<body>
<div id="header">
@@ -22,14 +22,6 @@
<div class="tab-content">
<div id="dirlisting">
- <div>
- <span class="name">File Name</span>
- <span class="name">File Size</span>
- </div>
- <div>
- <span class="name">File Name</span>
- <span class="name">File Size</span>
- </div>
</div>
</div>
<div class="tab-content">
@@ -52,6 +44,5 @@
</div>
</div>
</div>
- <script src="assets/cargohold.js"></script>
</body>
</html>