summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmed <git@gumx.cc>2026-06-17 15:45:51 +0300
committerAhmed <git@gumx.cc>2026-06-17 15:45:51 +0300
commit2faa1fede9cfe79e2bc93e3e5e2444e4f54cb32b (patch)
tree8cc2747ee551c51a075553f905156559ba0835dd
parent12b6af597d9f3c463fdae9e59e3f5a432bf6c004 (diff)
fix: style, docs, and few routes
-rw-r--r--_shared/style.css1
-rw-r--r--files.gumx.cc/body.html3
-rw-r--r--files.gumx.cc/upload.html83
-rw-r--r--hooks/post-receive8
-rw-r--r--irc.gumx.cc/body.html2
-rw-r--r--wk.fo/body.html8
-rw-r--r--wk.fo/pks/index.html3
-rw-r--r--wk.fo/upload.html83
8 files changed, 180 insertions, 11 deletions
diff --git a/_shared/style.css b/_shared/style.css
index 0f7e7cf..3efbeb1 100644
--- a/_shared/style.css
+++ b/_shared/style.css
@@ -16,7 +16,6 @@ code { font-size: 85%; }
hr { border: none; border-top: thin solid; margin: 1.25rem 0; }
header { margin-bottom: 1em; }
footer { margin-top: 3em; }
-a { color: inherit; }
@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } }
@media (max-width: 400px) { body { font-size: 0.8em; } h1 { font-size: 1.6em; } }
@media (prefers-color-scheme: dark) { html { filter: invert(1); } img { filter: invert(1); } }
diff --git a/files.gumx.cc/body.html b/files.gumx.cc/body.html
index 3c34353..19e0bff 100644
--- a/files.gumx.cc/body.html
+++ b/files.gumx.cc/body.html
@@ -1,6 +1,7 @@
<p>File hosting via <a href="https://github.com/mia-0/0x0">0x0</a>. Uploads require a token.</p>
<h2>upload</h2>
-<pre><code>curl -F "file=@photo.jpg" -H "Authorization: YOUR_TOKEN" https://files.gumx.cc/</code></pre>
+<p>Use the <a href="/upload">web form</a> or curl:</p>
+<pre><code>curl -F "file=@photo.jpg" -H "Authorization: Bearer YOUR_TOKEN" https://files.gumx.cc/</code></pre>
<p>Files expire after 24 hours by default. Max 256 MB.</p>
<h2>access</h2>
<p>Contact <a href="mailto:hi@gumx.cc">hi@gumx.cc</a> to request a token.</p>
diff --git a/files.gumx.cc/upload.html b/files.gumx.cc/upload.html
new file mode 100644
index 0000000..e82bd96
--- /dev/null
+++ b/files.gumx.cc/upload.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAiIGhlaWdodD0iMTQwIiBzdHJva2U9IiMwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iMTIyIiBzdHJva2UtZGFzaGFycmF5PSIyLDM4IiBkPSJtOSw3MGgxMjJNNzAsOXYxMjIiLz48cGF0aCBzdHJva2Utd2lkdGg9IjMzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Im03MCwzMGgwbTQwLDQwaDBtLTgwLDQwdjBtNDAsMGgwbTQwLDBoMCIvPjwvc3ZnPg==">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>files.gumx.cc / upload</title>
+<style>
+@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Regular.woff2); font-weight: normal; }
+@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Bold.woff2); font-weight: bold; }
+* { unicode-bidi: plaintext; box-sizing: border-box; }
+html { color: black; background-color: white; }
+body { font-family: "Kawkab Mono"; font-size: 16px; line-height: 1.4; margin: 0; padding: 4rem 0; min-height: 100%; overflow-wrap: break-word; }
+main, header, footer { max-width: 800px; margin-inline: auto; padding: 0 2rem; }
+h1, header, footer { text-align: center; }
+main { text-align: justify; }
+p, h2, h3, h4 { margin: 1em 0 0 0; }
+pre { margin: 1em 0; }
+pre code { border: thin solid; padding: 1em; display: block; text-align: start; overflow-x: scroll; }
+code { font-size: 85%; }
+hr { border: none; border-top: thin solid; margin: 1.25rem 0; }
+header { margin-bottom: 1em; }
+footer { margin-top: 3em; }
+input[type="text"], input[type="file"], button { font-family: inherit; font-size: inherit; border: thin solid; padding: 0.3em 0.6em; background: transparent; color: inherit; }
+input[type="text"] { width: 100%; }
+button { cursor: pointer; }
+#result { margin-top: 1em; word-break: break-all; }
+@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } }
+@media (prefers-color-scheme: dark) { html { filter: invert(1); } }
+</style>
+</head>
+<body>
+<header>
+<h1><a href="https://gumx.cc">gumx</a> / <a href="https://files.gumx.cc">files</a> / upload</h1>
+</header>
+<main>
+<p>Upload a file using your token. Files expire after 24 hours by default. Max 256 MB.</p>
+<h2>web form</h2>
+<p><input type="text" id="token" placeholder="token" autocomplete="off"></p>
+<p><input type="file" id="file"></p>
+<p><button onclick="upload()">upload</button></p>
+<p id="result"></p>
+<h2>curl</h2>
+<pre><code>curl -F "file=@photo.jpg" -H "Authorization: Bearer YOUR_TOKEN" https://files.gumx.cc/</code></pre>
+</main>
+<footer>
+<hr>
+<a href="https://twt.gumx.cc">twt</a> /
+<a href="https://git.gumx.cc">git</a> /
+<a href="https://mail.gumx.cc">mail</a> /
+<a href="https://irc.gumx.cc">irc</a> /
+<a href="https://files.gumx.cc">files</a> /
+<a href="https://vpn.gumx.cc">vpn</a> /
+<a href="https://pgp.gumx.cc">pgp</a> /
+<a href="https://demo.gumx.cc">demo</a> /
+<a href="https://wk.fo">wk.fo</a>
+</footer>
+<script>
+async function upload() {
+ const token = document.getElementById('token').value.trim();
+ const file = document.getElementById('file').files[0];
+ const result = document.getElementById('result');
+ if (!token) { result.textContent = 'enter a token'; return; }
+ if (!file) { result.textContent = 'choose a file'; return; }
+ result.textContent = 'uploading...';
+ try {
+ const fd = new FormData();
+ fd.append('file', file);
+ const r = await fetch('/', {
+ method: 'POST',
+ headers: { 'Authorization': 'Bearer ' + token },
+ body: fd
+ });
+ const text = await r.text();
+ if (!r.ok) { result.textContent = 'error ' + r.status + ': ' + text; return; }
+ result.innerHTML = '<a href="' + text.trim() + '">' + text.trim() + '</a>';
+ } catch (e) {
+ result.textContent = 'error: ' + e.message;
+ }
+}
+</script>
+</body>
+</html>
diff --git a/hooks/post-receive b/hooks/post-receive
index 3ecfd11..fe4f5f6 100644
--- a/hooks/post-receive
+++ b/hooks/post-receive
@@ -1,5 +1,6 @@
#!/bin/sh
set -e
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
WORK=/home/git/build/sites
@@ -33,8 +34,11 @@ for SITE in irc.gumx.cc vpn.gumx.cc mail.gumx.cc pgp.gumx.cc wk.fo twt.gumx.cc f
fi
done
+chmod +x /var/www/wk.fo/pks/cgi-bin/pgp-hkp.sh || true
+chmod +x /var/www/pgp.gumx.cc/cgi-bin/pgp-hkp.sh || true
+
# 404 page for sites outside this hook
-cp _shared/404.html /var/www/gumx.cc/404.html
-cp _shared/404.html /usr/share/webapps/cgit/404.html
+cp _shared/404.html /var/www/gumx.cc/404.html || true
+cp _shared/404.html /usr/share/webapps/cgit/404.html || true
echo "sites deployed"
diff --git a/irc.gumx.cc/body.html b/irc.gumx.cc/body.html
index 48440ca..0ddcf72 100644
--- a/irc.gumx.cc/body.html
+++ b/irc.gumx.cc/body.html
@@ -1,6 +1,6 @@
<p>Personal IRC server running <a href="https://ngircd.barton.de/">ngircd</a>, fronted by a <a href="https://soju.im/">Soju</a> bouncer. Access is by invitation.</p>
<h2>web client</h2>
-<p><a href="/chat">open gamja</a> — no account needed. Connect with any nick.</p>
+<p><a href="/chat">open gamja</a> — requires a soju account. Use your IRC credentials.</p>
<h2>connect via IRC client</h2>
<table>
<tr><th>server</th><td>irc.gumx.cc:6697 (TLS)</td></tr>
diff --git a/wk.fo/body.html b/wk.fo/body.html
index 158927c..ac05efb 100644
--- a/wk.fo/body.html
+++ b/wk.fo/body.html
@@ -1,16 +1,16 @@
<p>File sharing, IRC, VPN, and tools for friends. Invite required for most services.</p>
<h2>file sharing</h2>
-<p>Upload via HTTPS (token required):</p>
-<pre><code>curl -F "file=@photo.jpg" -H "Authorization: YOUR_TOKEN" https://wk.fo/</code></pre>
+<p>Upload via the <a href="/upload">web form</a> or curl (token required):</p>
+<pre><code>curl -F "file=@photo.jpg" -H "Authorization: Bearer YOUR_TOKEN" https://wk.fo/</code></pre>
<p>Files expire after 24 hours by default (max 48h with <code>expires</code> param). Max 256 MB.</p>
<h2>irc</h2>
-<p><a href="/chat">web client</a> — no account needed. Or connect directly:</p>
+<p><a href="/chat">web client</a> — requires a soju account. Or connect directly:</p>
<table>
<tr><th>server</th><td>wk.fo:6697 (TLS)</td></tr>
<tr><th>auth</th><td>SASL PLAIN (invite only)</td></tr>
</table>
<h2>pgp</h2>
-<p>Curated keyserver — keys added after in-person verification. <a href="/pgp">search or browse</a>.</p>
+<p>Curated keyserver — keys added after in-person verification. <a href="/pks/">search or browse</a>.</p>
<h2>vpn</h2>
<table>
<tr><th>server</th><td>wk.fo:51820 (UDP)</td></tr>
diff --git a/wk.fo/pks/index.html b/wk.fo/pks/index.html
index 2847b9f..727a6f2 100644
--- a/wk.fo/pks/index.html
+++ b/wk.fo/pks/index.html
@@ -21,7 +21,6 @@ code { font-size: 85%; }
hr { border: none; border-top: thin solid; margin: 1.25rem 0; }
header { margin-bottom: 1em; }
footer { margin-top: 3em; }
-a { color: inherit; }
input[type="text"], button { font-family: inherit; font-size: inherit; border: thin solid; padding: 0.3em 0.6em; background: transparent; color: inherit; }
button { cursor: pointer; }
@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } }
@@ -53,7 +52,7 @@ button { cursor: pointer; }
<a href="https://irc.gumx.cc">irc</a> /
<a href="https://files.gumx.cc">files</a> /
<a href="https://vpn.gumx.cc">vpn</a> /
-<a href="/pgp">pgp</a> /
+<a href="/pks/">pgp</a> /
<a href="https://demo.gumx.cc">demo</a> /
<a href="https://wk.fo">wk.fo</a>
</footer>
diff --git a/wk.fo/upload.html b/wk.fo/upload.html
new file mode 100644
index 0000000..6578afd
--- /dev/null
+++ b/wk.fo/upload.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNDAiIGhlaWdodD0iMTQwIiBzdHJva2U9IiMwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iMTIyIiBzdHJva2UtZGFzaGFycmF5PSIyLDM4IiBkPSJtOSw3MGgxMjJNNzAsOXYxMjIiLz48cGF0aCBzdHJva2Utd2lkdGg9IjMzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Im03MCwzMGgwbTQwLDQwaDBtLTgwLDQwdjBtNDAsMGgwbTQwLDBoMCIvPjwvc3ZnPg==">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>wk.fo / upload</title>
+<style>
+@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Regular.woff2); font-weight: normal; }
+@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Bold.woff2); font-weight: bold; }
+* { unicode-bidi: plaintext; box-sizing: border-box; }
+html { color: black; background-color: white; }
+body { font-family: "Kawkab Mono"; font-size: 16px; line-height: 1.4; margin: 0; padding: 4rem 0; min-height: 100%; overflow-wrap: break-word; }
+main, header, footer { max-width: 800px; margin-inline: auto; padding: 0 2rem; }
+h1, header, footer { text-align: center; }
+main { text-align: justify; }
+p, h2, h3, h4 { margin: 1em 0 0 0; }
+pre { margin: 1em 0; }
+pre code { border: thin solid; padding: 1em; display: block; text-align: start; overflow-x: scroll; }
+code { font-size: 85%; }
+hr { border: none; border-top: thin solid; margin: 1.25rem 0; }
+header { margin-bottom: 1em; }
+footer { margin-top: 3em; }
+input[type="text"], input[type="file"], button { font-family: inherit; font-size: inherit; border: thin solid; padding: 0.3em 0.6em; background: transparent; color: inherit; }
+input[type="text"] { width: 100%; }
+button { cursor: pointer; }
+#result { margin-top: 1em; word-break: break-all; }
+@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } }
+@media (prefers-color-scheme: dark) { html { filter: invert(1); } }
+</style>
+</head>
+<body>
+<header>
+<h1><a href="https://wk.fo">wk.fo</a> / upload</h1>
+</header>
+<main>
+<p>Upload a file using your token. Files expire after 24 hours by default. Max 256 MB.</p>
+<h2>web form</h2>
+<p><input type="text" id="token" placeholder="token" autocomplete="off"></p>
+<p><input type="file" id="file"></p>
+<p><button onclick="upload()">upload</button></p>
+<p id="result"></p>
+<h2>curl</h2>
+<pre><code>curl -F "file=@photo.jpg" -H "Authorization: Bearer YOUR_TOKEN" https://wk.fo/</code></pre>
+</main>
+<footer>
+<hr>
+<a href="https://twt.gumx.cc">twt</a> /
+<a href="https://git.gumx.cc">git</a> /
+<a href="https://mail.gumx.cc">mail</a> /
+<a href="https://irc.gumx.cc">irc</a> /
+<a href="https://files.gumx.cc">files</a> /
+<a href="https://vpn.gumx.cc">vpn</a> /
+<a href="/pks/">pgp</a> /
+<a href="https://demo.gumx.cc">demo</a> /
+<a href="https://wk.fo">wk.fo</a>
+</footer>
+<script>
+async function upload() {
+ const token = document.getElementById('token').value.trim();
+ const file = document.getElementById('file').files[0];
+ const result = document.getElementById('result');
+ if (!token) { result.textContent = 'enter a token'; return; }
+ if (!file) { result.textContent = 'choose a file'; return; }
+ result.textContent = 'uploading...';
+ try {
+ const fd = new FormData();
+ fd.append('file', file);
+ const r = await fetch('/', {
+ method: 'POST',
+ headers: { 'Authorization': 'Bearer ' + token },
+ body: fd
+ });
+ const text = await r.text();
+ if (!r.ok) { result.textContent = 'error ' + r.status + ': ' + text; return; }
+ result.innerHTML = '<a href="' + text.trim() + '">' + text.trim() + '</a>';
+ } catch (e) {
+ result.textContent = 'error: ' + e.message;
+ }
+}
+</script>
+</body>
+</html>