diff options
| author | Collin Williams <96917990+bluedragon1221@users.noreply.github.com> | 2026-10-02 11:23:45 -0500 |
|---|---|---|
| committer | Collin Williams <96917990+bluedragon1221@users.noreply.github.com> | 2026-10-02 11:23:45 -0500 |
| commit | b768ad0ca4208e8b3588b881067c43d85246e967 (patch) | |
| tree | 460dca5c6568307efc94e256186346be0271478a /modules/services/nixos/jta | |
| parent | 84364a52c3d7ca2f550cb3f7af56848405902bee (diff) | |
experiment with modular services; more unix domain sockets, clean up old code
Diffstat (limited to 'modules/services/nixos/jta')
| -rw-r--r-- | modules/services/nixos/jta/assignments.html | 148 | ||||
| -rw-r--r-- | modules/services/nixos/jta/default.nix | 48 | ||||
| -rw-r--r-- | modules/services/nixos/jta/go.mod | 5 | ||||
| -rw-r--r-- | modules/services/nixos/jta/go.sum | 2 | ||||
| -rw-r--r-- | modules/services/nixos/jta/index.html | 134 | ||||
| -rw-r--r-- | modules/services/nixos/jta/main.go | 142 | ||||
| -rw-r--r-- | modules/services/nixos/jta/md_template.html | 11 | ||||
| -rw-r--r-- | modules/services/nixos/jta/pkg.nix | 6 | ||||
| -rw-r--r-- | modules/services/nixos/jta/style.css | 172 |
9 files changed, 0 insertions, 668 deletions
diff --git a/modules/services/nixos/jta/assignments.html b/modules/services/nixos/jta/assignments.html deleted file mode 100644 index 3031a29..0000000 --- a/modules/services/nixos/jta/assignments.html +++ /dev/null @@ -1,148 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>jta@ganymede</title> - <link rel="stylesheet" href="style.css"> - <style> - body { - margin: 0; - max-width: none; - padding: 0; - background: var(--bg); - } - - header { - padding: 1.25rem 1.5rem; - text-align: center; - font-family: var(--font-heading); - font-size: var(--step-2); - line-height: 1.2; - letter-spacing: -0.01em; - font-weight: 700; - background: #020617; - border-bottom: 1px solid var(--border); - } - - .section { - padding: 1rem; - max-width: 1100px; - margin: 0 auto; - } - - .section-title { - font-size: var(--step-1); - line-height: 1.25; - letter-spacing: -0.01em; - margin: 1rem 0 0.5rem 0.5rem; - color: var(--text-soft); - } - - .container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); - gap: 1rem; - padding: 0 1rem 1rem 1rem; - } - - .card { - background: var(--card); - border: 1px solid var(--border); - border-radius: 16px; - padding: 1rem; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); - transition: transform 0.15s ease, box-shadow 0.15s ease; - cursor: pointer; - text-decoration: none; - color: var(--text); - display: block; - } - - .card:hover { - transform: translateY(-4px); - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.6); - } - - .title { - font-size: var(--step-1); - line-height: 1.3; - letter-spacing: -0.01em; - font-weight: 600; - margin-bottom: 0.5rem; - } - - .due { - font-size: var(--step--1); - color: var(--muted); - margin-bottom: 0.5rem; - } - - .desc { - font-size: var(--step--1); - color: var(--text-soft); - line-height: 1.55; - } - </style> - </head> - <body> - <header>Juksere Trives Aldri</header> - - <div id="app"></div> - - <script> - const app = document.getElementById("app"); - - function renderHome(data) { - const groups = {}; - data.forEach((item) => { - const cls = item.class || "Other"; - if (!groups[cls]) groups[cls] = []; - groups[cls].push(item); - }); - - Object.entries(groups).forEach(([cls, items]) => { - const section = document.createElement("div"); - section.className = "section"; - - const title = document.createElement("div"); - title.className = "section-title"; - title.textContent = cls; - section.appendChild(title); - - const container = document.createElement("div"); - container.className = "container"; - - items.forEach((item) => { - const card = document.createElement("a"); - card.className = "card"; - card.href = item.link || "#"; - - card.innerHTML = ` - <div class="title">${item.title}</div> - <div class="due">Due: ${item.due}</div> - ${item.description ? `<div class="desc">${item.description}</div>` : ""} - `; - - container.appendChild(card); - }); - - section.appendChild(container); - app.appendChild(section); - }); - } - - fetch("assignments.json") - .then((res) => { - if (!res.ok) { - throw new Error("Failed to load data"); - } - return res.json(); - }) - .then((data) => renderHome(data)) - .catch(() => { - app.innerHTML = "<p style='padding:1rem;'>Failed to load data.</p>"; - }); - </script> - </body> -</html> diff --git a/modules/services/nixos/jta/default.nix b/modules/services/nixos/jta/default.nix deleted file mode 100644 index 31f5bbd..0000000 --- a/modules/services/nixos/jta/default.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: let - cfg = config.collinux.services.jta; - package = pkgs.callPackage ./pkg.nix {}; -in { - config = lib.mkIf cfg.enable { - users.groups."jta" = {}; - users.users."jta" = { - isSystemUser = true; - group = "jta"; - extraGroups = ["fileserver"]; - - home = "/var/lib/jta"; - createHome = true; - homeMode = "755"; - }; - - systemd.services."jta" = { - restartIfChanged = true; - wants = ["network-online.target" "caddy.service"]; - after = ["network-online.target" "caddy.service"]; - - environment = { - PORT = toString cfg.port; - ROOT_DIR = "/media/jta"; - AUTH_PASSWORD_HASH = "550b6785c4bce2ab41a5e2eaa1eb43172d5abbd58237f1c78cd32cb190edc2c5"; # alfreddabuttlerwithtwots - }; - - serviceConfig = { - User = "jta"; - Type = "simple"; - - WorkingDirectory = "/var/lib/jta"; - ExecStart = "${package}/bin/jta"; - }; - - wantedBy = ["multi-user.target"]; - }; - - services.caddy.virtualHosts."jta.williamsfam.us.com".extraConfig = '' - reverse_proxy 127.0.0.1:${toString cfg.port} - ''; - }; -} diff --git a/modules/services/nixos/jta/go.mod b/modules/services/nixos/jta/go.mod deleted file mode 100644 index 8f27af1..0000000 --- a/modules/services/nixos/jta/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module git.ganymede/jta - -go 1.25.5 - -require github.com/yuin/goldmark v1.8.2 diff --git a/modules/services/nixos/jta/go.sum b/modules/services/nixos/jta/go.sum deleted file mode 100644 index 6a37955..0000000 --- a/modules/services/nixos/jta/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= -github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= diff --git a/modules/services/nixos/jta/index.html b/modules/services/nixos/jta/index.html deleted file mode 100644 index 96b6f5f..0000000 --- a/modules/services/nixos/jta/index.html +++ /dev/null @@ -1,134 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>JTA Login</title> - <link rel="stylesheet" href="style.css"> - <style> - body { - margin: 0; - min-height: 100vh; - display: grid; - place-items: center; - padding: 1rem; - background: var(--bg); - } - - .card { - width: min(520px, 100%); - background: var(--card); - border: 1px solid var(--border); - border-radius: 16px; - padding: 1.5rem; - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.38); - } - - h1 { - margin: 0 0 0.25rem 0; - text-align: center; - font-family: var(--font-heading); - font-size: var(--step-2); - line-height: 1.15; - font-weight: 700; - } - - p { - margin: 0.5rem 0 1.2rem 0; - text-align: center; - color: var(--text-soft); - line-height: 1.5; - } - - .actions { - display: flex; - justify-content: center; - gap: 0.75rem; - } - - button { - appearance: none; - border: 1px solid var(--border); - border-radius: 12px; - background: #0f172a; - color: var(--text); - padding: 0.65rem 1rem; - font-size: var(--step-0); - font-weight: 600; - cursor: pointer; - transition: transform 0.15s ease, box-shadow 0.15s ease; - } - - button:hover { - transform: translateY(-1px); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35); - } - - #status { - margin-top: 1rem; - text-align: center; - min-height: 1.25rem; - font-size: var(--step--1); - color: var(--text-soft); - } - </style> - </head> - <body> - <div class="card"> - <h1>JTA Portal</h1> - <p>Public landing page. Login is required to view assignments and all other pages.</p> - - <div class="actions"> - <button id="login-btn" type="button">Login</button> - </div> - - <div id="status" aria-live="polite"></div> - </div> - - <script> - const authCookieName = "jta_auth"; - const status = document.getElementById("status"); - const loginButton = document.getElementById("login-btn"); - - function toHex(buffer) { - const bytes = new Uint8Array(buffer); - return Array.from(bytes) - .map((b) => b.toString(16).padStart(2, "0")) - .join(""); - } - - async function sha256Hex(input) { - const data = new TextEncoder().encode(input); - const digest = await crypto.subtle.digest("SHA-256", data); - return toHex(digest); - } - - function setAuthCookie(hashValue) { - const maxAge = 10 * 365 * 24 * 60 * 60; - document.cookie = `${authCookieName}=${hashValue}; Path=/; Max-Age=${maxAge}; SameSite=Lax`; - } - - async function loginWithPrompt() { - const password = prompt("Enter password"); - if (password === null) { - status.textContent = "Login cancelled."; - return; - } - - status.textContent = "Logging in..."; - - const hashValue = await sha256Hex(password); - setAuthCookie(hashValue); - - status.textContent = "Success. Redirecting..."; - window.location.href = "/assignments"; - } - - loginButton.addEventListener("click", () => { - loginWithPrompt().catch(() => { - status.textContent = "Network error while logging in."; - }); - }); - </script> - </body> -</html> diff --git a/modules/services/nixos/jta/main.go b/modules/services/nixos/jta/main.go deleted file mode 100644 index e7b5a0f..0000000 --- a/modules/services/nixos/jta/main.go +++ /dev/null @@ -1,142 +0,0 @@ -package main - -import ( - "bytes" - _ "embed" - "html/template" - "net/http" - "os" - "path/filepath" - "strings" - - "github.com/yuin/goldmark" - "github.com/yuin/goldmark/extension" -) - -// Configure Goldmark with GitHub Flavored Markdown -var mdParser = goldmark.New( - goldmark.WithExtensions( - extension.GFM, - ), -) - -//go:embed md_template.html -var mdTemplate string - -//go:embed style.css -var styleCSS string - -//go:embed index.html -var indexHTML string - -//go:embed assignments.html -var assignmentsHTML string - -var pageTmpl = template.Must(template.New("md_template.html").Parse(mdTemplate)) - -const authCookieName = "jta_auth" - -func isAuthenticated(w http.ResponseWriter, r *http.Request) bool { - expectedHash := strings.TrimSpace(os.Getenv("AUTH_PASSWORD_HASH")) - if expectedHash == "" { - return false - } - - // Try cookie first - if cookie, err := r.Cookie(authCookieName); err == nil { - providedHash := strings.TrimSpace(cookie.Value) - if providedHash != "" && strings.EqualFold(providedHash, expectedHash) { - return true - } - } - - // Fallback to URL parameter - paramHash := strings.TrimSpace(r.URL.Query().Get(authCookieName)) - if paramHash != "" && strings.EqualFold(paramHash, expectedHash) { - http.SetCookie(w, &http.Cookie{ - Name: authCookieName, - Value: paramHash, - Path: "/", - HttpOnly: true, // Security: Protects against XSS cookie theft - Secure: false, // Set to true if you are hosting over HTTPS - SameSite: http.SameSiteLaxMode, // Prevents CSRF vulnerabilities - }) - return true - } - - return false -} - -func serveMarkdown(w http.ResponseWriter, r *http.Request) { - // Prevent path traversal - clean := filepath.Clean(r.URL.Path) - path := filepath.Join(rootDir, strings.TrimPrefix(clean, "/")) - - mdBytes, err := os.ReadFile(path) - if err != nil { - http.NotFound(w, r) - return - } - - var buf bytes.Buffer - if err := mdParser.Convert(mdBytes, &buf); err != nil { - http.Error(w, "failed to render markdown", 500) - return - } - - w.Header().Set("Content-Type", "text/html; charset=utf-8") - - pageTmpl.Execute(w, map[string]template.HTML{ - "Content": template.HTML(buf.String()), - }) -} - -var rootDir = "." - -func main() { - if envRootDir := os.Getenv("ROOT_DIR"); envRootDir != "" { - rootDir = envRootDir - } - - fs := http.FileServer(http.Dir(rootDir)) - port := os.Getenv("PORT") - if port == "" { - port = "8080" - } - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/" || r.URL.Path == "/index.html" { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.Write([]byte(indexHTML)) - return - } - - if r.URL.Path == "/style.css" { - w.Header().Set("Content-Type", "text/css; charset=utf-8") - w.Write([]byte(styleCSS)) - return - } - - if !isAuthenticated(w, r) { - http.Error(w, "unauthorized", http.StatusUnauthorized) - return - } - - if r.URL.Path == "/assignments" || r.URL.Path == "/assignments.html" { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.Write([]byte(assignmentsHTML)) - return - } - - // Intercept markdown files - if strings.HasSuffix(r.URL.Path, ".md") { - serveMarkdown(w, r) - return - } - - // Otherwise serve normally - fs.ServeHTTP(w, r) - }) - - http.ListenAndServe(":"+port, nil) -} diff --git a/modules/services/nixos/jta/md_template.html b/modules/services/nixos/jta/md_template.html deleted file mode 100644 index daa57b7..0000000 --- a/modules/services/nixos/jta/md_template.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <title>JTA</title> - <link rel="stylesheet" href="/style.css"> - </head> - <body> - {{.Content}} - </body> -</html> diff --git a/modules/services/nixos/jta/pkg.nix b/modules/services/nixos/jta/pkg.nix deleted file mode 100644 index c8f7d77..0000000 --- a/modules/services/nixos/jta/pkg.nix +++ /dev/null @@ -1,6 +0,0 @@ -{pkgs ? import <nixpkgs> {system = "x86_64-linux";}, ...}: -pkgs.buildGoModule { - name = "jta"; - src = ./.; - vendorHash = "sha256-aeJYO52ALbGxyTFPw6eGCmPlmuCvbXEoX9gcXatN29E="; -} diff --git a/modules/services/nixos/jta/style.css b/modules/services/nixos/jta/style.css deleted file mode 100644 index ec09ca9..0000000 --- a/modules/services/nixos/jta/style.css +++ /dev/null @@ -1,172 +0,0 @@ -:root { - --bg: #0f172a; - --card: #1e293b; - --accent: #22c55e; - --text: #e2e8f0; - --text-soft: #cbd5e1; - --muted: #94a3b8; - --border: #334155; - - --font-body: "Avenir Next", "Segoe UI", "Noto Sans", "Helvetica Neue", sans-serif; - --font-heading: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - - --step--1: clamp(0.88rem, 0.86rem + 0.12vw, 0.96rem); - --step-0: clamp(1rem, 0.98rem + 0.16vw, 1.08rem); - --step-1: clamp(1.14rem, 1.08rem + 0.26vw, 1.28rem); - --step-2: clamp(1.3rem, 1.2rem + 0.45vw, 1.56rem); - --step-3: clamp(1.7rem, 1.45rem + 1.1vw, 2.25rem); -} - -/* Base layout */ -body { - font-family: var(--font-body); - font-size: var(--step-0); - background: var(--bg); - color: var(--text); - max-width: 850px; - margin: 2rem auto; - padding: 1.5rem; - line-height: 1.7; - text-rendering: optimizeLegibility; -} - -/* Headings */ -h1 { - font-family: var(--font-heading); - font-size: var(--step-3); - line-height: 1.15; - font-weight: 700; - letter-spacing: -0.02em; - text-align: center; - margin: 0 0 1.25rem; -} - -h2 { - font-family: var(--font-heading); - margin-top: 2.25rem; - margin-bottom: 0.75rem; - font-size: var(--step-2); - line-height: 1.25; - letter-spacing: -0.01em; -} - -h3 { - font-family: var(--font-heading); - margin-top: 1.5rem; - margin-bottom: 0.6rem; - font-size: var(--step-1); - line-height: 1.3; - color: var(--text-soft); -} - -/* Text */ -p { - color: var(--text); - margin-bottom: 1rem; - max-width: 65ch; -} - -/* Ordered list (questions) */ -ol { - padding-left: 1.2rem; - max-width: 65ch; -} - -/* Answer list */ -ul { - list-style: none; - padding-left: 1.2rem; - max-width: 65ch; -} - -ul li { - display: flex; - align-items: flex-start; - gap: 0.6rem; - padding: 0.35rem 0.4rem; - border-radius: 8px; - transition: background 0.15s ease; -} - -/* Checkbox styling */ -input[type="checkbox"] { - accent-color: var(--accent); - transform: scale(1.2); - margin-top: 0.2rem; - pointer-events: none; -} - -/* Correct answers */ -li:has(input[type="checkbox"]:checked) { - color: #86efac; - font-weight: 500; -} - -/* Code blocks */ -pre { - font-family: var(--font-mono); - font-size: var(--step--1); - line-height: 1.6; - background: #020617; - padding: 1rem; - border-radius: 10px; - overflow-x: auto; - margin: 1rem 0; - max-width: 65ch; -} - -code { - font-family: var(--font-mono); - font-size: 0.95em; - background: #020617; - padding: 2px 6px; - border-radius: 6px; -} - -img { - display: block; - max-width: 100%; - height: auto; - margin: 1rem 0; - border-radius: 10px; -} - -p > img, -li > img { - margin: 0.75rem 0; -} - -a > img { - transition: transform 0.15s ease, box-shadow 0.15s ease; -} - -a:hover > img { - transform: translateY(-1px); - box-shadow: 0 8px 20px rgba(2, 6, 23, 0.45); -} - -img[width], -img[height] { - width: auto; - height: auto; - max-width: 100%; -} - -/* Links */ -a { - color: #93c5fd; - text-decoration-line: underline; - text-decoration-thickness: 2px; - text-underline-offset: 0.14em; - text-decoration-color: rgba(147, 197, 253, 0.45); -} - -a:hover { - text-decoration-color: currentColor; -} - -/* Optional: make everything feel tighter and nicer */ -* { - box-sizing: border-box; -} |
