Add apps subdomain (#36)

* Add apps subdomain

* Fix formatting
This commit is contained in:
Patrick Stevens
2025-01-29 18:34:49 +00:00
committed by GitHub
parent 58b958ae86
commit 656b93b248
6 changed files with 179 additions and 9 deletions

View File

@@ -95,6 +95,7 @@ type Address =
type WellKnownSubdomain =
| Nextcloud
| Apps
| Gitea
| Radicale
| Rss
@@ -106,18 +107,20 @@ type WellKnownSubdomain =
override this.ToString () =
match this with
| Nextcloud -> "nextcloud"
| Gitea -> "gitea"
| Radicale -> "calendar"
| Rss -> "rss"
| Grafana -> "grafana"
| Woodpecker -> "woodpecker"
| WoodpeckerAgent -> "woodpecker-agent"
| PureGym -> "puregym"
| Whisper -> "whisper"
| WellKnownSubdomain.Nextcloud -> "nextcloud"
| WellKnownSubdomain.Apps -> "apps"
| WellKnownSubdomain.Gitea -> "gitea"
| WellKnownSubdomain.Radicale -> "calendar"
| WellKnownSubdomain.Rss -> "rss"
| WellKnownSubdomain.Grafana -> "grafana"
| WellKnownSubdomain.Woodpecker -> "woodpecker"
| WellKnownSubdomain.WoodpeckerAgent -> "woodpecker-agent"
| WellKnownSubdomain.PureGym -> "puregym"
| WellKnownSubdomain.Whisper -> "whisper"
static member Parse (s : string) =
match s with
| "apps" -> WellKnownSubdomain.Apps
| "nextcloud" -> WellKnownSubdomain.Nextcloud
| "gitea" -> WellKnownSubdomain.Gitea
| "calendar" -> WellKnownSubdomain.Radicale

View File

@@ -0,0 +1,32 @@
{
lib,
config,
...
}: {
options = {
services.apps-config = {
domain = lib.mkOption {
type = lib.types.str;
example = "example.com";
description = lib.mdDoc "Top-level domain to configure";
};
subdomain = lib.mkOption {
type = lib.types.str;
example = "apps";
description = lib.mdDoc "Subdomain in which to put apps";
};
port = lib.mkOption {
type = lib.types.port;
description = lib.mdDoc "App server localhost port";
default = 9521;
};
};
};
config = {
services.nginx.virtualHosts."${config.services.apps-config.subdomain}.${config.services.apps-config.domain}" = {
forceSSL = true;
enableACME = true;
root = ./static;
};
};
}

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<h1>Apps</h1>
<p>This is a collection of single-page applications, mostly vomited out by Claude to solve small tasks.</p>
<ul>
<li><a href="/middle_word.html">Compute the number of words in a string and the middle word.</a></li>
<li><a href="/sha256sum.html">Compute SHA256sums, as one might wish to do to verify someone's precommitment to a piece of text.</a></li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<body>
<textarea id="input" rows="10" cols="50"></textarea><br>
<button onclick="analyzeText()">Ok</button>
<div id="result"></div>
<script>
function analyzeText() {
const text = document.getElementById('input').value;
const words = text.trim().split(' ').filter(word => word.length > 0);
const count = words.length;
const result = `Word count: ${count}`;
if (count % 2 === 1) {
const middleWord = words[Math.floor(count/2)];
document.getElementById('result').textContent =
`${result}\nMiddle word: ${middleWord}`;
} else {
document.getElementById('result').textContent = result;
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<title>Base64 Decoder and SHA256 Calculator</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
.container {
display: flex;
flex-direction: column;
gap: 20px;
}
.input-group {
display: flex;
flex-direction: column;
gap: 10px;
}
textarea {
width: 100%;
min-height: 100px;
padding: 8px;
margin-bottom: 10px;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#hashOutput {
font-family: monospace;
padding: 8px;
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
word-break: break-all;
}
</style>
</head>
<body>
<div class="container">
<div class="input-group">
<label for="base64Input">Base64 Input:</label>
<textarea id="base64Input" placeholder="Enter base64 encoded text here"></textarea>
<button onclick="decodeBase64()">Decode Base64</button>
</div>
<div class="input-group">
<label for="textInput">Text Input:</label>
<textarea id="textInput" placeholder="Enter text or see decoded base64 here"></textarea>
<button onclick="calculateSHA256()">Calculate SHA256</button>
</div>
<div class="input-group">
<label for="hashOutput">SHA256 Hash:</label>
<div id="hashOutput"></div>
</div>
</div>
<script>
function decodeBase64() {
try {
const base64Input = document.getElementById('base64Input').value;
const decoded = atob(base64Input);
document.getElementById('textInput').value = decoded;
} catch (error) {
alert('Invalid base64 input: ' + error.message);
}
}
async function calculateSHA256() {
try {
const text = document.getElementById('textInput').value;
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
document.getElementById('hashOutput').textContent = hashHex;
} catch (error) {
alert('Error calculating hash: ' + error.message);
}
}
</script>
</body>
</html>

View File

@@ -7,6 +7,7 @@
in {
imports = [
./sops.nix
./apps/apps.nix
./radicale/radicale-config.nix
./gitea/gitea-config.nix
./miniflux/miniflux.nix
@@ -43,6 +44,8 @@ in {
services.prometheus-config.domain-exporter-domains = [userConfig.domain];
services.puregym-config.domain = userConfig.domain;
services.puregym-config.subdomain = "puregym";
services.apps-config.subdomain = "apps";
services.apps-config.domain = userConfig.domain;
# services.whisper-config.domain = userConfig.domain;
# services.whisper-config.subdomain = "whisper";