Inital commit

This commit is contained in:
2026-06-23 14:07:17 -04:00
commit 3fb016894b
8 changed files with 361 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
hjem

26
Makefile Normal file
View File

@@ -0,0 +1,26 @@
GO ?= go
.PHONY: build lint install uninstall install-openrc uninstall-openrc
all: build
build:
$(GO) build -o hjem
lint:
$(GO) vet ./...
$(GO) fmt ./...
install:
cp hjem /usr/local/bin
uninstall:
$(RM) /usr/local/bin/hjem
install-openrc:
cp hjem.initd /etc/init.d/hjem
user="$${SUDO_USER:-$${DOAS_USER}}"; \
echo "command_user=$$user" >> /etc/init.d/hjem
uninstall-openrc:
$(RM) /etc/init.d/hjem

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Hjem
Minimal homepage server.

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module hjem
go 1.26.4

BIN
hjem Executable file

Binary file not shown.

230
hjem.html Normal file
View File

@@ -0,0 +1,230 @@
<html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>newtab</title>
<style>
:root {
color-scheme: dark;
--bg: #282433;
--fg: #eeeeee;
--muted: color-mix(in srgb, CanvasText 54%, Canvas 46%);
--faint: color-mix(in srgb, CanvasText 18%, Canvas 82%);
--line: color-mix(in srgb, CanvasText 14%, Canvas 86%);
--accent: LinkText;
--fallback-bg: #0b0d10;
--fallback-fg: #c8cdd2;
--fallback-muted: #6f7882;
--fallback-line: #20242a;
--fallback-accent: #9aa7b2;
}
* {
box-sizing: border-box;
}
html {
height: 100%;
background: var(--bg, var(--fallback-bg));
}
body {
min-height: 100%;
margin: 0;
display: grid;
place-items: center;
background: var(--bg, var(--fallback-bg));
color: var(--fg, var(--fallback-fg));
font: 15px/1.45 Iosevka, monospace;
letter-spacing: 0.01em;
}
main {
width: min(640px, calc(100vw - 48px));
display: grid;
gap: 28px;
}
header {
display: flex;
align-items: baseline;
justify-content: space-between;
border-bottom: 1px solid var(--line, var(--fallback-line));
padding-bottom: 12px;
}
.label {
color: var(--muted, var(--fallback-muted));
}
#clock {
font-size: 28px;
font-weight: 500;
letter-spacing: 0.08em;
font-variant-numeric: tabular-nums;
}
form {
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
gap: 12px;
border-bottom: 1px solid var(--line, var(--fallback-line));
padding-bottom: 10px;
}
form span {
color: var(--muted, var(--fallback-muted));
user-select: none;
}
input {
width: 100%;
border: 0;
outline: 0;
background: transparent;
color: var(--fg, var(--fallback-fg));
font: inherit;
caret-color: var(--accent, var(--fallback-accent));
}
input::placeholder {
color: var(--muted, var(--fallback-muted));
opacity: 1;
}
nav {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1px;
background: var(--line, var(--fallback-line));
border: 1px solid var(--line, var(--fallback-line));
}
a {
display: block;
padding: 14px 16px;
background: var(--bg, var(--fallback-bg));
color: var(--fg, var(--fallback-fg));
text-decoration: none;
}
a:hover,
a:focus-visible {
color: var(--accent, var(--fallback-accent));
outline: 1px solid var(--accent, var(--fallback-accent));
outline-offset: -1px;
}
kbd {
color: var(--muted, var(--fallback-muted));
font: inherit;
float: right;
}
footer {
color: var(--muted, var(--fallback-muted));
font-size: 13px;
border-top: 1px solid var(--line, var(--fallback-line));
padding-top: 12px;
}
@media (max-width: 520px) {
nav {
grid-template-columns: 1fr;
}
header {
display: grid;
gap: 4px;
}
}
</style>
</head>
<body>
<main>
<header>
<div>
<div class="label">{{.OS}}</div>
<div>{{.Email}}</div>
</div>
<time id="clock" aria-label="current time">00:00</time>
</header>
<form id="search" autocomplete="off">
<span>search</span>
<input
id="query"
name="q"
type="search"
placeholder="duckduckgo"
spellcheck="false"
autofocus
>
</form>
<nav aria-label="quick links">
<a href="https://youtube.com">youtube <kbd>y</kbd></a>
<a href="https://git.fsdproject.org">Fsd Git <kbd>f</kbd></a>
<a href="https://git.vxserver.dev">Personal Git <kbd>g</kbd></a>
</nav>
<footer>
an idiot admires complexity, a genius admires simplicity.
</footer>
</main>
<script>
const clock = document.getElementById("clock");
const input = document.getElementById("query");
const form = document.getElementById("search");
const links = {
y: "https://youtube.com",
f: "https://git.fsdproject.org",
g: "https://git.vxserver.dev"
};
function tick() {
const now = new Date();
clock.textContent = now.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: true
});
}
tick();
setInterval(tick, 1000);
form.addEventListener("submit", event => {
event.preventDefault();
const q = input.value.trim();
if (!q) return;
location.href = "https://duckduckgo.com/?q=" + encodeURIComponent(q);
});
window.addEventListener("keydown", event => {
const typing = document.activeElement === input;
if (event.key === "/" && !typing) {
event.preventDefault();
input.focus();
return;
}
if (!typing && links[event.key]) {
location.href = links[event.key];
}
});
</script>
</body>
</html>

12
hjem.initd Executable file
View File

@@ -0,0 +1,12 @@
#!/sbin/openrc-run
name="Hjem"
description="Hjem homepage service"
command="/usr/local/bin/hjem"
command_background="yes"
pidfile="/run/hjem.pid"
depend() {
need net localmount
}

86
main.go Normal file
View File

@@ -0,0 +1,86 @@
package main
import (
"bufio"
_ "embed"
"flag"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"text/template"
)
//go:embed hjem.html
var hjemTemplate string
var port = flag.Int("p", 1437, "set the port for hjem to use")
type HjemData struct {
OS string
Email string
}
func main() {
flag.Parse()
t, err := template.New("hjem").Parse(hjemTemplate)
if err != nil {
panic(err)
}
user_os := "Unknown"
file, err := os.Open("/etc/os-release")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
if scanner.Scan() {
user_os = strings.TrimRight(strings.TrimLeft(scanner.Text(), "NAME='"), "'")
}
user_email := "unknown@example.com"
file, err = os.Open(filepath.Join(func() string {
hostname, err := os.UserHomeDir()
if err != nil {
panic(err)
}
return hostname
}(), ".gitconfig"))
if err != nil {
panic(err)
}
defer file.Close()
scanner = bufio.NewScanner(file)
for scanner.Scan() {
text := scanner.Text()
if strings.Contains(text, "email = ") {
user_email = strings.TrimLeft(strings.TrimSpace(text), "email = ")
break
}
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err := t.ExecuteTemplate(w, "hjem", HjemData{
OS: user_os,
Email: user_email,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
fmt.Printf("Web server is available at http://0.0.0.0:%d\n", *port)
fmt.Println("Press Ctrl+C to stop")
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
}