mirror of
https://github.com/Smaug123/PulumiConfig
synced 2025-10-05 16:48:39 +00:00
@@ -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
|
||||
|
32
PulumiWebServer/Nix/apps/apps.nix
Normal file
32
PulumiWebServer/Nix/apps/apps.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
}
|
12
PulumiWebServer/Nix/apps/static/index.html
Normal file
12
PulumiWebServer/Nix/apps/static/index.html
Normal 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>
|
||||
|
25
PulumiWebServer/Nix/apps/static/middle_word.html
Normal file
25
PulumiWebServer/Nix/apps/static/middle_word.html
Normal 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>
|
95
PulumiWebServer/Nix/apps/static/sha256sum.html
Normal file
95
PulumiWebServer/Nix/apps/static/sha256sum.html
Normal 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>
|
@@ -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";
|
||||
|
||||
|
Reference in New Issue
Block a user