From a51e2c24a927fea0af92677d8fba7015bd4e4ccd Mon Sep 17 00:00:00 2001 From: Maks Jopek Date: Sat, 11 Jul 2026 00:48:14 +0200 Subject: Initial commit --- cgi-bin/delete-file.py | 15 +++++++ cgi-bin/list-files.py | 9 ++++ cgi-bin/upload.py | 23 ++++++++++ public/index.html | 48 ++++++++++++++++++++ public/script.js | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ puszcza.jopek.eu.conf | 37 ++++++++++++++++ uploads/.gitkeep | 0 7 files changed, 248 insertions(+) create mode 100755 cgi-bin/delete-file.py create mode 100755 cgi-bin/list-files.py create mode 100755 cgi-bin/upload.py create mode 100644 public/index.html create mode 100644 public/script.js create mode 100644 puszcza.jopek.eu.conf create mode 100644 uploads/.gitkeep diff --git a/cgi-bin/delete-file.py b/cgi-bin/delete-file.py new file mode 100755 index 0000000..cdb6588 --- /dev/null +++ b/cgi-bin/delete-file.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +from urllib.parse import parse_qs + +print("Content-Type: application/json; charset=utf-8") +print("") + +query = parse_qs(os.environ.get('QUERY_STRING', '')) +if "filename" in query: + filename = query["filename"][0] + if "/" in filename: + exit() + os.remove("uploads/" + filename) + +print('{"ok": true}') diff --git a/cgi-bin/list-files.py b/cgi-bin/list-files.py new file mode 100755 index 0000000..15d6391 --- /dev/null +++ b/cgi-bin/list-files.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import json, os + +print("Content-Type: application/json; charset=utf-8") +print("") + +dir_list = os.listdir("uploads") + +print(json.dumps(dir_list)) diff --git a/cgi-bin/upload.py b/cgi-bin/upload.py new file mode 100755 index 0000000..f137cc7 --- /dev/null +++ b/cgi-bin/upload.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +import sys, os +from email.parser import BytesParser +from email.policy import default + +print("Content-Type: text/html; charset=utf-8") +print("") + +CONTENT_LENGTH = os.environ.get("CONTENT_LENGTH") +CONTENT_TYPE = os.environ.get("CONTENT_TYPE") +if CONTENT_TYPE is None or CONTENT_LENGTH is None: + exit() + +length = int(CONTENT_LENGTH) +raw = b"Content-Type: " + CONTENT_TYPE.encode() + b"\r\n\r\n" + sys.stdin.buffer.read(length) +msg = BytesParser(policy=default).parsebytes(raw) + +for part in msg.iter_parts(): + name = part.get_filename() + if name is None: + continue + with open(os.path.join("uploads", os.path.basename(name)), "wb") as f: + f.write(part.get_payload(decode=True)) diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..c7a2291 --- /dev/null +++ b/public/index.html @@ -0,0 +1,48 @@ + + + + + + + + + + + +
+ + +

+ Aktualne pliki + +
+ + diff --git a/public/script.js b/public/script.js new file mode 100644 index 0000000..0bfe734 --- /dev/null +++ b/public/script.js @@ -0,0 +1,116 @@ +const DB_DIR_KEY = "DIR_HANDLE"; +const bar = document.querySelector("#bar"); +const btn = document.querySelector("#btn"); +const stdoutEl = document.querySelector("#stdout"); +const fileListEl = document.querySelector("#fileList"); +const db = await idb(); + +const stdout = (msg) => (stdoutEl.textContent = msg); + +if (await db.has(DB_DIR_KEY)) { + btn.textContent = "Wgraj pliki z folderu " + (await db.get(DB_DIR_KEY)).name; +} else { + btn.textContent = "Wybierz folder"; +} + +async function handleUpload() { + let dirHandle; + if (await db.has(DB_DIR_KEY)) { + dirHandle = await db.get(DB_DIR_KEY); + dirHandle.requestPermission(); + } else { + dirHandle = await window.showDirectoryPicker(); + } + + const filesUploaded = []; + const formData = new FormData(); + for await (const [_, fileHandle] of dirHandle.entries()) { + if (fileHandle.kind !== "file") continue; + formData.append("files", await fileHandle.getFile()); + filesUploaded.push(fileHandle.name); + } + + await upload(formData); + + if ((await db.has(DB_DIR_KEY)) === false) { + await db.set(DB_DIR_KEY, dirHandle); + } + stdout( + `Pliki\n\n[${filesUploaded.join(", ")}]\n\nz folderu\n\n${dirHandle.name}\n\nwgrane`, + ); +} +btn.onclick = handleUpload; + +async function listFiles() { + const files = await fetch("./cgi-bin/list-files.py").then((res) => + res.json(), + ); + const children = []; + for (const file of files) { + const li = document.createElement("li"); + li.textContent = file; + const b = document.createElement("button"); + b.textContent = "Usun"; + b.onclick = () => deleteFile(file); + li.appendChild(b); + children.push(li); + } + fileListEl.replaceChildren(...children); +} +listFiles(); + +async function deleteFile(fileName) { + await fetch( + "./cgi-bin/delete-file.py?filename=" + encodeURIComponent(fileName), + { + method: "POST", + }, + ); + await listFiles(); +} + +async function upload(formData, onProgress) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + + bar.value = 0; + + xhr.upload.addEventListener("progress", (e) => { + if (e.lengthComputable) { + const percent = (e.loaded / e.total) * 100; + bar.value = percent; + } + }); + + xhr.addEventListener("load", resolve); + + xhr.open("POST", "./cgi-bin/upload.py"); + xhr.send(formData); + }); +} + +async function idb(dbName = "app", store = "kv") { + const db = await new Promise((resolve, reject) => { + const req = indexedDB.open(dbName, 1); + req.onupgradeneeded = () => req.result.createObjectStore(store); + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); + + const run = (mode, fn) => + new Promise((resolve, reject) => { + const t = db.transaction(store, mode); + const req = fn(t.objectStore(store)); + t.oncomplete = () => resolve(req?.result); + t.onerror = () => reject(t.error); + }); + + return { + get: (k) => run("readonly", (s) => s.get(k)), + set: (k, v) => run("readwrite", (s) => s.put(v, k)), + del: (k) => run("readwrite", (s) => s.delete(k)), + keys: () => run("readonly", (s) => s.getAllKeys()), + clear: () => run("readwrite", (s) => s.clear()), + has: (k) => run("readonly", (s) => s.count(k)).then((n) => n > 0), + }; +} diff --git a/puszcza.jopek.eu.conf b/puszcza.jopek.eu.conf new file mode 100644 index 0000000..c450018 --- /dev/null +++ b/puszcza.jopek.eu.conf @@ -0,0 +1,37 @@ +server { + server_name puszcza.jopek.eu www.puszcza.jopek.eu; + listen 443 ssl default_server; + listen [::]:443 ssl default_server; + http2 on; + index index.html index.php; + + location /admin { + auth_basic "Uploaded files"; + auth_basic_user_file "/var/www/puszcza.jopek.eu/http.passwords"; + root /var/www/puszcza.jopek.eu/public; + } + location /admin/cgi-bin { + auth_basic "Uploaded files"; + auth_basic_user_file "/var/www/puszcza.jopek.eu/http.passwords"; + fastcgi_pass unix:/run/fcgiwrap.sock; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME /var/www/puszcza.jopek.eu/cgi-bin/$fastcgi_script_name; + } + + location / { + root /var/www/puszcza.jopek.eu/cgi-bin/uploads; + } + + ssl_certificate /etc/acme.sh/jopek.eu_ecc/fullchain.cer; + ssl_certificate_key /etc/acme.sh/jopek.eu_ecc/jopek.eu.key; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; +} + +server { + server_name puszcza.jopek.eu www.puszcza.jopek.eu; + listen 80; + listen [::]:80; + + return 301 https://$host$request_uri; +} diff --git a/uploads/.gitkeep b/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 -- cgit v1.3.1