feat: initial

This commit is contained in:
Lain Iwakura 2026-05-19 22:36:24 +03:00
commit 90b46d44f1
Signed by: lain
GPG key ID: 8160466B2E8D1441
7 changed files with 227 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
result

27
flake.lock generated Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1779102034,
"narHash": "sha256-vZJZjLo513IeI8hjzHFc6TDezUd4uCE2Eq4SNO3DNNg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "687f05a9184cad4eaf905c48b63649e3a86f5433",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

55
flake.nix Normal file
View file

@ -0,0 +1,55 @@
{
description = "cultist.club landing page";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
};
outputs = { self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-darwin"
];
forAllSystems = nixpkgs.lib.genAttrs systems;
in
{
packages = forAllSystems (system:
let
pkgs = import nixpkgs { inherit system; };
cultist-website = pkgs.buildGoModule {
pname = "cultist-website";
version = "0.1.0";
src = ./.;
vendorHash = null;
nativeBuildInputs = [ pkgs.makeWrapper ];
ldflags = [ "-s" "-w" ];
postInstall =
if pkgs.stdenv.hostPlatform.isLinux then
''
wrapProgram $out/bin/cultist-website \
--prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.procps ]}
''
else
"";
};
in
{
default = cultist-website;
cultist-website = cultist-website;
});
apps = forAllSystems (system: {
default = {
type = "app";
program = "${self.packages.${system}.default}/bin/cultist-website";
};
});
devShells = forAllSystems (system: {
default = import nixpkgs { inherit system; }.mkShell {
packages = [ (import nixpkgs { inherit system; }).go ];
};
});
};
}

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module cultist.club/website
go 1.25.9

62
main.go Normal file
View file

@ -0,0 +1,62 @@
package main
import (
"embed"
"html"
"io/fs"
"log/slog"
"net/http"
"os"
"os/exec"
"strings"
)
//go:embed web/*
var webFS embed.FS
func main() {
addr := os.Getenv("LISTEN_ADDR")
if addr == "" {
addr = "127.0.0.1:8080"
}
static, err := fs.Sub(webFS, "web")
if err != nil {
slog.Error("embed fs", "err", err)
os.Exit(1)
}
indexHTML, err := fs.ReadFile(static, "index.html")
if err != nil {
slog.Error("read index", "err", err)
os.Exit(1)
}
mux := http.NewServeMux()
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
uptime := uptimeLine()
body := strings.ReplaceAll(string(indexHTML), "{{uptime}}", html.EscapeString(uptime))
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
_, _ = w.Write([]byte(body))
})
mux.Handle("GET /style.css", http.FileServer(http.FS(static)))
slog.Info("listening", "addr", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
slog.Error("serve", "err", err)
os.Exit(1)
}
}
func uptimeLine() string {
out, err := exec.Command("uptime").Output()
if err != nil {
return "uptime unavailable"
}
return strings.TrimSpace(string(out))
}

21
web/index.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>cultist.club</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>Welcome home. Again.</h1>
<div class="links">
<a href="https://src.cultist.club">src</a>
<a href="ircs://irc.cultist.club:6697">irc</a>
</div>
<div class="uptime">
<marquee>{{uptime}}</marquee>
</div>
</div>
</body>
</html>

58
web/style.css Normal file
View file

@ -0,0 +1,58 @@
:root {
--bg: #0a0a0a;
--fg: #e0e0e0;
--muted: #888;
--border: #333;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: var(--bg);
color: var(--fg);
font-family: monospace;
}
.container {
text-align: center;
padding: 2rem;
}
h1 {
font-size: 1.4rem;
font-weight: normal;
letter-spacing: 0.05em;
}
.links {
margin-top: 1.5rem;
font-size: 0.85rem;
}
.links a {
margin: 0 0.5rem;
color: var(--muted);
}
.uptime {
margin-top: 2rem;
overflow: hidden;
max-width: 420px;
border: 1px solid var(--border);
padding: 0.3rem 0;
}
.uptime marquee {
font-size: 0.85rem;
color: var(--muted);
}