summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaks Jopek <maks@jopek.eu>2026-07-11 00:48:14 +0200
committerMaks Jopek <maks@jopek.eu>2026-07-11 00:49:00 +0200
commita51e2c24a927fea0af92677d8fba7015bd4e4ccd (patch)
tree3fd60d5e476b0039d12e7ba70c0282b447f52447
downloadpuszcza.jopek.eu-a51e2c24a927fea0af92677d8fba7015bd4e4ccd.tar.gz
puszcza.jopek.eu-a51e2c24a927fea0af92677d8fba7015bd4e4ccd.tar.zst
puszcza.jopek.eu-a51e2c24a927fea0af92677d8fba7015bd4e4ccd.zip
Initial commit
-rwxr-xr-xcgi-bin/delete-file.py15
-rwxr-xr-xcgi-bin/list-files.py9
-rwxr-xr-xcgi-bin/upload.py23
-rw-r--r--public/index.html48
-rw-r--r--public/script.js116
-rw-r--r--puszcza.jopek.eu.conf37
-rw-r--r--uploads/.gitkeep0
7 files changed, 248 insertions, 0 deletions
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 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title></title>
+ <!-- <link href="css/style.css" rel="stylesheet"> -->
+ <script type="module" src="./script.js"></script>
+ <style>
+ body {
+ margin: 0;
+ height: calc(100vh - 3rem);
+ padding-top: 3rem;
+ background-color: darkslategray;
+ color: white;
+ }
+ .cont {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
+ #btn {
+ width: 10rem;
+ padding-block: 2rem;
+ border-radius: 1rem;
+ }
+ progress {
+ margin-top: 1rem;
+ height: 2rem;
+ width: 10rem;
+ }
+ #stdout {
+ white-space: pre;
+ text-align: center;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="cont">
+ <button type="button" id="btn"></button>
+ <progress min="0" max="100" value="0" id="bar"></progress>
+ <p id="stdout"></p>
+ Aktualne pliki
+ <ul id="fileList"></ul>
+ </div>
+ </body>
+</html>
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
--- /dev/null
+++ b/uploads/.gitkeep