summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2026-01-09 08:07:03 -0500
committerSam Anthony <sam@samanthony.xyz>2026-01-09 08:07:03 -0500
commit2ad28cbbfb4287164e7877923291bb18519688fc (patch)
tree7a7dac539d2085cb72cd357246620f7f2021f5e8
parentebf4af075b194229dd1cf3163b1e3cec1f1d3a0d (diff)
downloadsamanthony.xyz-2ad28cbbfb4287164e7877923291bb18519688fc.zip
replace repo contents with revamped site
-rw-r--r--.gitignore4
-rw-r--r--Makefile17
-rw-r--r--archetypes/default.md6
-rw-r--r--content/_index.html65
-rw-r--r--content/articles/_index.md7
-rw-r--r--content/articles/post-1.md14
-rw-r--r--content/articles/tools-i-use.md74
-rw-r--r--go.mod5
-rw-r--r--go.sum2
-rw-r--r--htdocs/base.html46
-rw-r--r--htdocs/css/style.css83
-rw-r--r--htdocs/css/syntax.css19
-rw-r--r--htdocs/index.html97
-rw-r--r--htdocs/software/index.html20
-rw-r--r--htdocs/software/samanthony.xyz.html50
-rw-r--r--hugo.toml25
-rw-r--r--layouts/baseof.html19
-rw-r--r--layouts/home.html3
-rw-r--r--layouts/list.html21
-rw-r--r--layouts/page.html11
-rw-r--r--layouts/term.html7
-rw-r--r--server.go208
-rw-r--r--themes/aoidh/assets/aoidh.css183
-rw-r--r--themes/aoidh/hugo.toml4
-rw-r--r--themes/aoidh/layouts/_partials/footer.html17
-rw-r--r--themes/aoidh/layouts/_partials/head.html6
-rw-r--r--themes/aoidh/layouts/_partials/header.html1
-rw-r--r--themes/aoidh/layouts/_partials/menu.html40
-rw-r--r--themes/aoidh/layouts/_partials/terms.html23
-rw-r--r--util.go103
30 files changed, 528 insertions, 652 deletions
diff --git a/.gitignore b/.gitignore
index 241531c..c4b387a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-/devserver
-/webserver
+.hugo_build.lock
+public/
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a393724..0000000
--- a/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-build_dev: tidy format
- go build -o devserver
-
-serve_dev: build_dev
- ./devserver --chroot ./ --root /htdocs/ --host localhost
-
-build: tidy format
- GOOS=openbsd GOARCH=amd64 go build -o webserver
-
-live:
- rsync -rtvzP ./htdocs/ sam@samanthony.xyz:/var/www/htdocs/samanthony.xyz/
-
-format:
- gofmt -s -w .
-
-tidy:
- go mod tidy
diff --git a/archetypes/default.md b/archetypes/default.md
new file mode 100644
index 0000000..631acf5
--- /dev/null
+++ b/archetypes/default.md
@@ -0,0 +1,6 @@
++++
+title = '{{ replace .File.ContentBaseName "-" " " | title }}'
+date = '{{ .Date }}'
+tags = []
+draft = true
++++
diff --git a/content/_index.html b/content/_index.html
new file mode 100644
index 0000000..5ed65a4
--- /dev/null
+++ b/content/_index.html
@@ -0,0 +1,65 @@
+---
+title: ""
+date: 2025-12-23T17:12:55-03:30
+draft: false
+---
+
+<blockquote><code>
+/* _nop() has been deprecated; use __nop() */<br>
+#define _nop() __nop()
+</code>
+<cite>&mdash;excerpt from <a href="https://www.microchip.com/en-us/tools-resources/develop/mplab-xc-compilers/xc8"><code>pic.h</code></a></cite>
+</blockquote>
+
+<h1>About</h1>
+<table>
+ <thead>
+ <tr>
+ <th>Personal information</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Name</td>
+ <td>Sam Anthony</td>
+ </tr>
+ <tr>
+ <td>Occupation</td>
+ <td>Computer programmer</td>
+ </tr>
+ <tr>
+ <td>Education</td>
+ <td>BCompSc, <a href="https://www.concordia.ca/">Concordia University</a> (expected to graduate 2026)</td>
+ </tr>
+ <tr>
+ <td>Previous employers</td>
+ <td>
+ <a href="https://wheeltec.ca/">WheelTec</a>
+ <br>
+ <a href="https://c-core.ca/">C-CORE</a>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<table>
+ <thead>
+ <tr>
+ <th>Contact</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Email</td>
+ <td><a href="mailto:sam@samanthony.xyz">sam@samanthony.xyz</a></td>
+ </tr>
+ <tr>
+ <td>Phone</td>
+ <td>+1 (709) 746-7582</td>
+ </tr>
+ </tbody>
+</table>
+<h1>Biography</h1>
+<img src="img/headshot.jpg" alt="Sam Anthony">
+<p>TODO: biography</p>
diff --git a/content/articles/_index.md b/content/articles/_index.md
new file mode 100644
index 0000000..bc3438a
--- /dev/null
+++ b/content/articles/_index.md
@@ -0,0 +1,7 @@
++++
+title = 'Articles'
+date = 2025-12-23T08:30:00-07:00
+draft = false
++++
+
+Tempor est exercitation ad qui pariatur quis adipisicing aliquip nisi ea consequat ipsum occaecat. Nostrud consequat ullamco laboris fugiat esse esse adipisicing velit laborum ipsum incididunt ut enim. Dolor pariatur nulla quis fugiat dolore excepteur. Aliquip ad quis aliqua enim do consequat.
diff --git a/content/articles/post-1.md b/content/articles/post-1.md
new file mode 100644
index 0000000..54f4f32
--- /dev/null
+++ b/content/articles/post-1.md
@@ -0,0 +1,14 @@
++++
+title = 'Post 1'
+date = 2025-12-23T09:00:00-07:00
+tags = ['red']
+draft = false
++++
+
+Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. This is a [link](http://git.samanthony.xyz) within body text. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.
+
+Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.
+
+Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.
+
+Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.
diff --git a/content/articles/tools-i-use.md b/content/articles/tools-i-use.md
new file mode 100644
index 0000000..952d782
--- /dev/null
+++ b/content/articles/tools-i-use.md
@@ -0,0 +1,74 @@
++++
+title = 'Tools I Use'
+date = '2026-01-02T15:24:53-03:30'
+tags = []
+draft = true
++++
+
+Here is a list of some of the tools I use day-to-day.
+
+
+# Software
+
+- [Gentoo Linux](https://www.gentoo.org/) ---
+ One of the most solid barebones Linux distros I know of.
+ I use it on my desktop and laptop computers.
+- [OpenBSD](https://www.openbsd.org/) ---
+ Secure server operating system.
+ I use it to serve this website.
+
+ Unlike a GNU/Linux distro, OpenBSD is a single homogeneous system.
+ Everything makes sense and the documentation is really good.
+ Reminds me of [Plan 9](https://plan9.io/plan9/) in that respect.
+
+- [Acme](http://acme.cat-v.org/) ---
+ Originally developed for Plan 9, Acme is [Rob Pike's](https://commandcenter.blogspot.com/) user interface for programmers.
+ It's what we now call an IDE.
+ I'm writing this article in Acme right now.
+
+ Every piece of text in Acme can be a executed or piped into/out-of a script.
+ Very powerful.
+- [Dwm](https://dwm.suckless.org/) ---
+ Nice tiling window manager for X11.
+ Goes well with other Suckless accoutrements.
+- [Syncthing](https://syncthing.net/) ---
+ Syncthing is a sort of distributed filesystem.
+ I use it to synchronize files between my laptop, desktop, and phone.
+
+
+# Hardware
+
+- [Thinkpad T420s](https://www.thinkwiki.org/wiki/Category:T420s) ---
+ I've had this laptop for a few years now; no complaints.
+
+ I replaced the hard drive with an SSD and threw some extra DDR3 in there.
+ The 10+ year old 4-thread Sandy Bridge i5 is actually fine.
+ I'm waiting for someone to make a [serious multithreaded](https://netlib.org/utk/papers/advanced-computers/tera.html) RISC-V CPU, but unfortunately everyone seems to be obsessed with high clock speeds and out-of-order-execution chips that use as much die space and power as possible.
+ That [Esperanto ET-SoC-1](https://youtu.be/LmUu-lN7D0k) looked promising, but apparently they went out of business or something?
+
+ Anyway, the T420s has a sturdy magnesium frame, a good keyboard, and a three-button touchpad which is essential for Acme and CAD programs---I don't know how people live without one.
+
+- iPod mini 1G ---
+ Hard to find a phone with a headphone jack these days.
+ The mini has one, as well as physical buttons and a touch rotary encoder---much nicer than using a touchscreen imo.
+ I want to get a full size iPod 1G with the physical scroll wheel at some point.
+
+ I replaced the battery with a new one and replaced the tiny (both physically and in terms of storage space) hard drive with a 128GB SD card.
+ Running [RockBox](https://www.rockbox.org/).
+ Works mint.
+
+- Casio F-91W ---
+ Cheap wristwatch that keeps time---what can I say?
+ If it's good enough for [al-Qaeda](https://en.wikipedia.org/wiki/Casio_F-91W#Usage_in_terrorism) then it's good enough for me.
+
+ Supposedly this is the most-sold watch in the world.
+ Casio has been making it since 1989.
+
+- [USBtin](https://www.fischl.de/usbtin/) ---
+ Simple USB-to-CAN interface by Thomas Fischl.
+ Works with [SocketCAN](https://docs.kernel.org/networking/can.html).
+ Used to test and debug systems that incorporate a CAN bus, like [this](http://git.samanthony.xyz/can-gauge-interface.git/).
+
+- [Espotek Labrador](https://espotek.com/labrador/) ---
+ Combined oscilloscope, signal generator, power supply, logic analyzer, and multimeter.
+ Obviously a tiny $30 board is not as good as real lab equipment, but it's small and cheap and good enough for now.
diff --git a/go.mod b/go.mod
deleted file mode 100644
index a128ff8..0000000
--- a/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module git.samanthony.xyz/samanthony.xyz
-
-go 1.18
-
-require golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 7f2d82d..0000000
--- a/go.sum
+++ /dev/null
@@ -1,2 +0,0 @@
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/htdocs/base.html b/htdocs/base.html
deleted file mode 100644
index f96c9c7..0000000
--- a/htdocs/base.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!doctype html>
-<html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- <!-- Child templates can choose to overwrite the default title -->
- {{ define "title" }}<title>samanthony.xyz</title>{{ end }}
- {{ template "title" }}
-
- <link rel="stylesheet" href="/css/style.css">
- <!-- Child templates can apply their own styles -->
- {{ define "style" }}{{ end }}
- {{ template "style" }}
- <!-- Code syntax highlighting -->
- <link rel="stylesheet" href="/css/syntax.css">
- </head>
- <body>
- <nav>
- {{ $this_section := .Nav.ThisSection }}
- {{ range .Nav.Links }}
- {{ if eq .Href $this_section }}
- <a class="this-section" href="{{ .Href }}">
- /<span class="label">{{ .Label }}</span>
- </a>
- {{else }}
- <a href="{{ .Href }}">
- |<span class="label">{{ .Label }}</span>
- </a>
- {{ end }}
- {{ end }}
- <hr>
- </nav>
- <main>
- {{ template "body_content" }}
- </main>
- <footer>
- <hr>
- <p>Copyright 2022 Sam Anthony</p>
- <p>
- samanthony.xyz is free software, licensed under the
- <a href="https://www.gnu.org/licenses/gpl.txt">
- GNU GPL.
- </a>
- </p>
- </body>
-</html>
diff --git a/htdocs/css/style.css b/htdocs/css/style.css
deleted file mode 100644
index 325f87e..0000000
--- a/htdocs/css/style.css
+++ /dev/null
@@ -1,83 +0,0 @@
-:root {
- --fg-color: white;
- --bg-color: black;
- --highlight-color: #4acaa4;
-}
-
-html { height: 100%; }
-
-body {
- display: flex;
- flex-direction: column;
- height: 100%;
-
- background-color: var(--bg-color);
- color: var(--fg-color);
-
- font-family: sans-serif;
-}
-
-main {
- margin-bottom: auto;
- align-self: center;
- max-width: 80em;
- width: 95vw;
-}
-
-.hanging-indent {
- padding-left: 2em;
- text-index: -2em;
-}
-
-header :first-child { margin-bottom: 0px; }
-
-hr {
- margin: 0px;
- color: var(--fg-color);
-}
-
-ul { padding-left: 2em; }
-
-/* Tables */
-table { padding-top: 1em; }
-th { text-align: left; }
-td:first-child { padding-right: 0.5em; }
-
-/* Links */
-a {
- color: var(--highlight-color);
- text-decoration: none;
-}
-a:hover { text-decoration: underline 0.055em; }
-
-/* Quotes */
-blockquote p { margin: 0px; }
-blockquote p:first-child { font-style: italic; }
-blockquote p:nth-child(2) {
- text-indent: 2em;
-}
-
-/* Nav */
-body { margin: 0px; }
-nav { background-color: var(--highlight-color); }
-nav a {
- margin-left: 0.2em;
-
- font-size: 1.6em;
- font-weight: normal;
-
- color: var(--fg-color);
-}
-nav a:first-child .label { font-weight: bold; }
-nav .label { font-size: 0.7em; }
-nav .this-section { font-style: italic; }
-
-/* Footer */
-footer {
- background-color: var(--highlight-color);
- text-align: center;
-}
-footer a {
- color: var(--fg-color);
- font-style: italic;
-}
diff --git a/htdocs/css/syntax.css b/htdocs/css/syntax.css
deleted file mode 100644
index f425178..0000000
--- a/htdocs/css/syntax.css
+++ /dev/null
@@ -1,19 +0,0 @@
-code {
- border: 0.055em solid var(--highlight-color);
- border-radius: 0.5em;
- padding-top: 0.2em;
- padding-bottom: 0.1em;
- padding-left: 0.5em;
- padding-right: 0.5em;
-
- display: inline-block;
- vertical-align: middle;
-
- background-color: #282828;
-}
-
-/* HTML */
-/* Tag */ code[data-lang="html"] .t { color: var(--highlight-color); }
-/* Element */ code[data-lang="html"] .e { color: #9b5712; }
-/* AttributeName */ code[data-lang="html"] .an { color: #91931b; }
-/* AttributeValue */ code[data-lang="html"] .av { color: red; }
diff --git a/htdocs/index.html b/htdocs/index.html
deleted file mode 100644
index 821d693..0000000
--- a/htdocs/index.html
+++ /dev/null
@@ -1,97 +0,0 @@
-{{ define "body_content" }}
- <header>
- <h1>Why This Site Exists</h2>
- <hr>
- </header>
-
- <p>
- Most of the modern Web sucks. This is my futile attempt at changing that.
- </p>
-
- <p>
- Many sites these days try to solve problems that don't exist using improper
- tools and techniques to do so. The classic example is JavaScript;
- client-side scripting can be quite useful <em>in certain situations</em>.
- The problem, however, is that most situations are not these situations.
- </p>
-
- <p>
- The vast majority of websites could be simple HTML( + CSS) pages and gain all
- the benefits that go along with that, namely <em>simplicity</em>,
- <em>speed/performance</em> and
- <a href="http://harmful.cat-v.org/Blog/2015/04/24/0/">
- <em>reliability</em></a>.
- Instead, people choose to complicate things.
- </p>
-
- <blockquote><p>"Install this plugin!"</p></blockquote>
- <blockquote><p>"Use this framework!"</p></blockquote>
-
- <p>
- Rather than fixing the underlying problem by stripping back the layers to
- find that we really just want to render some HTML, we instead pile more
- on top, obscuring and worsening whatever problem we think we're trying to
- solve.
- </p>
-
- <blockquote cite="https://suckless.org/sucks/web/">
- <p>
- "Millions of jobs are based on outputting HTML in an inefficient way."
- </p>
- <p>&mdash; <a href="https://suckless.org/sucks/web/">suckless.org</a></p>
- </blockquote>
-
- <p>
- Some common excuses for "I need to use x:"
- <ul>
- <li>
- "It's responsive"
- <br>
- <p style="margin: 0px;" class="hanging-indent">
- Setting this meta tag:
- <code data-lang="html">
- <span class="t">&lt;<span class="e">meta</span>
- <span class="an">name</span>=<span class="av">"viewport"</span>
- <span class="an">content</span>=<span class="av">"width=device-width, initial-scale=1.0"</span>&gt;
- </span>
- </code>
- along with some very basic CSS is all that's required for a
- responsive site that looks good on any screen.
- </p>
- </li>
- <li>
- "It looks nicer"
- <p style="margin: 0px;" class="hanging-indent">
- Java<em>Script</em>. Cascading
- <em>Style</em> Sheets. See the problem?
- Don't use scripts for styling. Also, no, it doesn't look nicer.
- </p>
- </li>
- <li>
- "It's makes for a better user experience"
- <p style="margin: 0px" class="hanging-indent">
- If you like waiting seconds for a page to load before scrolling an
- entire screen height with a parallaxed background just to read two
- words then yes, that is a good experience.
- Personally, I would rather not have to hover my cursor through a maze
- of eight nested, animated drop-down menus while being bombarded by
- popups asking for personal information and telling me that the site
- uses
- <a href="https://lcamtuf.blogspot.com/2010/10/http-cookies-or-how-not-to-design.html">
- cookies</a>.
- </p>
- </li>
- </ul>
- </p>
-
- <header>
- <h1>What Can We Do?</h1>
- <hr>
- </header>
-
- <p>
- Start your own website where you can complain about stuff too! Anyone can
- and everyone should. With
- <a href="https://landchad.net">all the information that's out there</a>
- it's never been easier to help fix the Web by creating your own site.
-{{ end }}
diff --git a/htdocs/software/index.html b/htdocs/software/index.html
deleted file mode 100644
index ecf8d95..0000000
--- a/htdocs/software/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{{ define "title" }}
- <title>software | samanthony.xyz</title>
-{{ end }}
-
-{{ define "body_content" }}
- <header>
- <h1>Software Hosted Here</h1>
- <hr>
- </header>
- <table>
- <tr>
- <th>name</th>
- <th>description</th>
- </tr>
- <tr>
- <td><a href="samanthony.xyz.html">samanthony.xyz</a></th>
- <td>This website</th>
- </tr>
- </table>
-{{ end }}
diff --git a/htdocs/software/samanthony.xyz.html b/htdocs/software/samanthony.xyz.html
deleted file mode 100644
index dc0c38c..0000000
--- a/htdocs/software/samanthony.xyz.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{{ define "title" }}
- <title>software | samanthony.xyz</title>
-{{ end }}
-
-{{ define "style" }}
-<style>
- h1, h2 { margin-bottom: 0px; }
- h2 {
- margin-top: 0px;
- padding-left: 1em;
- color: grey;
- }
-</style>
-{{ end }}
-
-{{ define "body_content" }}
- <header>
- <h1>samanthony.xyz</h1>
- <h2>This website</h2>
- <hr>
- </header>
- <p>
- Get a copy of the source code with
- <code>git clone git://samanthony.xyz/samanthony.xyz.git</code>.
- </p>
- <p>
- The site uses HTML templates to avoid tedium and for the ability to make
- small changes across the entire site without editing multiple files. It uses
- the html/template package included in Go's standard library.
- </p>
- <p>
- A server is required to serve these templates. Once again Go provides with
- the built in http package. This makes writing a web server extremely easy.
- All you have to do is write a function to handle incoming requests and call
- http.ListenAndServe(). Painless.
- </p>
- <p>
- Since I'm running OpenBSD, TLS can be dealt with external to the webserver
- by using relayd. As an aside, I encourage anyone looking for an operating
- system to give OpenBSD a look. It's been an absolute pleasure to work with
- so far. As long as you don't expect it to be exactly like a Linux-based
- system (it's not), then not only is it more secure, but also
- <em>easier to use</em> than something Linux-based. Even for a noob like
- myself. Sane design and good documentation go a long way.
- </p>
- <p>
- Inspiration was taken from httpd and the server runs chroot'd. When
- serving content it also drops down to an unprivileged user with setuid.
- </p>
-{{ end }}
diff --git a/hugo.toml b/hugo.toml
new file mode 100644
index 0000000..26150a4
--- /dev/null
+++ b/hugo.toml
@@ -0,0 +1,25 @@
+baseURL = 'https://www.samanthony.xyz/'
+languageCode = 'en-us'
+title = "Sam Anthony"
+theme = 'aoidh'
+
+[params]
+ [params.author]
+ name = "Sam Anthony"
+ email = "sam@samanthony.xyz"
+
+[menus]
+ [[menus.main]]
+ name = 'Home'
+ pageRef = '/'
+ weight = 1
+
+ [[menus.main]]
+ name = 'Articles'
+ pageRef = '/articles'
+ weight = 2
+
+ [[menus.main]]
+ name = 'Projects'
+ url = 'http://git.samanthony.xyz/'
+ weight = 3
diff --git a/layouts/baseof.html b/layouts/baseof.html
new file mode 100644
index 0000000..e5fd07d
--- /dev/null
+++ b/layouts/baseof.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="{{ site.Language.LanguageCode }}">
+<head>
+ {{ partial "head.html" . }}
+</head>
+<body>
+ <div class="content">
+ <header>
+ {{ partial "header.html" . }}
+ </header>
+ <main>
+ {{ block "main" . }}{{ end }}
+ </main>
+ <footer>
+ {{ partial "footer.html" . }}
+ </footer>
+ </div>
+</body>
+</html>
diff --git a/layouts/home.html b/layouts/home.html
new file mode 100644
index 0000000..e0e8308
--- /dev/null
+++ b/layouts/home.html
@@ -0,0 +1,3 @@
+{{ define "main" }}
+ {{ .Content }}
+{{ end }}
diff --git a/layouts/list.html b/layouts/list.html
new file mode 100644
index 0000000..cc6c7be
--- /dev/null
+++ b/layouts/list.html
@@ -0,0 +1,21 @@
+{{ define "main" }}
+ <h1>{{ .Title }}</h1>
+ <table>
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Title</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{ range .Pages }}
+ {{- $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" -}}
+ {{- $dateHuman := .Date | time.Format "2006-01-02" -}}
+ <tr>
+ <td><time datetime="{{ $dateMachine }}">{{ $dateHuman }}</time></td>
+ <td><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></td>
+ </tr>
+ {{ end }}
+ </tbody>
+ </table>
+{{ end }}
diff --git a/layouts/page.html b/layouts/page.html
new file mode 100644
index 0000000..8b8eb16
--- /dev/null
+++ b/layouts/page.html
@@ -0,0 +1,11 @@
+{{ define "main" }}
+ {{- $dateMachine := .Date | time.Format "2006-01-02T15:04:05-07:00" -}}
+ {{- $dateHuman := .Date | time.Format "2006-01-02" -}}
+ <div>
+ <h1 class="title">{{ .Title }}</h1>
+ <time datetime="{{ $dateMachine }}">{{ $dateHuman }}</time>
+ </div>
+
+ {{ .Content }}
+ {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
+{{ end }}
diff --git a/layouts/term.html b/layouts/term.html
new file mode 100644
index 0000000..c2e7875
--- /dev/null
+++ b/layouts/term.html
@@ -0,0 +1,7 @@
+{{ define "main" }}
+ <h1>{{ .Title }}</h1>
+ {{ .Content }}
+ {{ range .Pages }}
+ <h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
+ {{ end }}
+{{ end }}
diff --git a/server.go b/server.go
deleted file mode 100644
index 6296574..0000000
--- a/server.go
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
-Copyright 2022 Sam Anthony
-
-This file is part of samanthony.xyz.
-
-samanthony.xyz is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-
-samanthony.xyz is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-samanthony.xyz. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "flag"
- "fmt"
- "golang.org/x/sys/unix"
- "html/template"
- "io/fs"
- "log"
- "net"
- "net/http"
- "os"
- "path"
- fp "path/filepath"
- "strings"
-)
-
-// Flags
-var (
- host = "localhost"
- port = "80"
- chroot = "/var/www/"
- user = "www"
- group = "www"
- root = "/htdocs/samanthony.xyz/"
-)
-
-func init() {
- flag.StringVar(&host, "host", host, "")
- flag.StringVar(&port, "port", port, "")
- flag.StringVar(&chroot, "chroot", chroot, "")
- flag.StringVar(&user, "user", user, "")
- flag.StringVar(&group, "group", group, "")
- flag.StringVar(&root, "root", root, "")
-
- flag.Parse()
-}
-
-// Must lookup the hostname before entering the chroot.
-var addr = ""
-
-func init() {
- // host is an ip address
- if ip := net.ParseIP(host); ip != nil {
- addr = ip.String()
- } else { // host is a domain name
- addrs, err := net.LookupHost(host)
- if err != nil {
- log.Fatal(err)
- }
- for _, a := range addrs {
- if ip := net.ParseIP(a); ip != nil {
- if v4 := ip.To4(); v4 != nil {
- addr = v4.String()
- }
- }
- }
- if addr == "" {
- log.Fatalf("No ipv4 address bound to %s", host)
- }
- }
-}
-
-var (
- uid int
- gid int
-)
-
-func init() {
- var err error
- uid, err = uidOf(user)
- if err != nil {
- log.Fatal(err)
- }
- gid, err = gidOf(group)
- if err != nil {
- log.Fatal(err)
- }
-}
-
-// Enter chroot
-func init() {
- if err := unix.Chroot(chroot); err != nil {
- log.Fatalf("chroot: %s: %v", chroot, err)
- }
-}
-
-// Build templates
-var tmpl = make(map[string]*template.Template)
-
-func init() {
- err := fp.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
- if fp.Clean(path) == fp.Clean(root) ||
- fp.Ext(path) != ".html" ||
- path == fp.Join(root, "base.html") {
- return nil
- }
- label := path[len(fp.Clean(root)):]
- tmpl[label] = template.Must(template.ParseFiles(
- fp.Join(root, "base.html"), path))
- return nil
- })
- if err != nil {
- log.Fatal(err)
- }
-}
-
-// Template data
-type Page struct {
- Nav Nav
-}
-
-type Nav struct {
- ThisSection string
- Links []NavLink
-}
-
-type NavLink struct {
- Href string
- Label string
-}
-
-var nav = Nav{
- Links: []NavLink{
- {"/", "samanthony.xyz"},
- {"/software/", "software"},
- },
-}
-
-func rootHandler(w http.ResponseWriter, r *http.Request) {
- if err := dropPerms(uid, gid); err != nil {
- log.Println(err)
- code := http.StatusInternalServerError
- http.Error(w, http.StatusText(code), code)
- return
- }
-
- reqPath := r.URL.Path
-
- // If request directory, serve index.html.
- // ie. /software -> /software/index.html
- if info, err := os.Stat(fp.Join(root, reqPath)); err == nil {
- if info.IsDir() {
- reqPath = path.Join(reqPath, "index.html")
- }
- } else if os.IsNotExist(err) {
- http.NotFound(w, r)
- return
- } else {
- log.Println(err)
- code := http.StatusInternalServerError
- http.Error(w, http.StatusText(code), code)
- return
- }
-
- if t, ok := tmpl[reqPath]; ok {
- thisSection := ""
- for _, link := range nav.Links {
- if strings.HasPrefix(reqPath, link.Href) {
- thisSection = link.Href
- }
- }
- nav := nav
- nav.ThisSection = thisSection
- page := Page{nav}
-
- err := t.Execute(w, page)
- if err != nil {
- log.Println(err)
- code := http.StatusInternalServerError
- http.Error(w, http.StatusText(code), code)
- return
- }
- } else {
- http.ServeFile(w, r, fp.Join(root, reqPath))
- }
-}
-
-func main() {
- http.HandleFunc("/", rootHandler)
- http.Handle("/.well-known/acme-challenge/",
- http.StripPrefix(
- "/.well-known/acme-challenge/",
- http.FileServer(http.Dir("/acme/")),
- ),
- )
-
- log.Printf("Listening on %s:%s\n", addr, port)
- log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%s", addr, port), nil))
-}
diff --git a/themes/aoidh/assets/aoidh.css b/themes/aoidh/assets/aoidh.css
new file mode 100644
index 0000000..51571f5
--- /dev/null
+++ b/themes/aoidh/assets/aoidh.css
@@ -0,0 +1,183 @@
+:root {
+ --paper-color: #ffffea;
+ --light-yellow: #eeee9e;
+ --dark-yellow: #99994c;
+ --light-blue: #eaffff;
+ --dark-blue: #8888cc;
+ --light-gray: #eee;
+ --dark-gray: #888;
+
+ --parspace: .6em;
+}
+
+html {
+ background: var(--light-gray);
+}
+
+body {
+ margin: 1rem auto;
+ padding: .5rem 1rem .25rem;
+ max-width: 60rem;
+ border: 1px solid var(--dark-yellow);
+ box-shadow: 2px 2px var(--dark-yellow);
+}
+
+body {
+ line-height: 1.35em;
+ font-family: serif;
+ text-rendering: optimizeLegibility;
+ color: #000;
+ background: #fff;
+}
+
+.content {
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 45rem;
+}
+
+header {
+ margin-bottom: 2rem;
+}
+
+nav ul {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
+ gap: .4rem .8rem;
+ padding: 0;
+}
+
+nav li {
+ display: inline-block;
+}
+
+nav a {
+ display: inline-block;
+ width: 100%;
+ text-align: center;
+ vertical-align: middle;
+ white-space: nowrap;
+ color: #000;
+ border: .1rem solid var(--dark-blue);
+ box-shadow: .1rem .1rem var(--dark-blue);
+}
+
+li.nav-active a {
+ font-weight: bold;
+ box-shadow: .2rem .2rem var(--dark-yellow);
+}
+
+li.nav-ancestor a {
+ font-weight: bold;
+}
+
+nav a:active {
+ box-shadow: 0 0;
+}
+
+p {
+ text-align: justify;
+ hyphens: auto;
+ text-justify: inter-word;
+ margin-top: 0;
+ margin-bottom: var(--parspace);
+}
+
+a {
+ text-decoration: none;
+ border: 1px solid transparent;
+}
+
+a:hover {
+ text-decoration: none;
+ color: #000;
+ background: var(--light-blue);
+}
+
+a:active {
+ text-decoration: none;
+ color: #000;
+ background: var(--light-blue);
+ border: 1px solid var(--dark-blue);
+}
+
+.title {
+ font-size: 1.4rem;
+}
+
+h1 {
+ font-size: 1.3rem;
+}
+
+h2 {
+ font-size: 1.2rem;
+}
+
+h3 {
+ font-size: 1.1rem;
+}
+
+h1, h2, h3 {
+ margin-block-end: .4em; /* above */
+ margin-block-start: .8em; /* below */
+}
+
+table {
+ border-collapse: collapse;
+ text-align: left;
+ margin-bottom: 1rem;
+}
+
+thead tr {
+ border: 1px solid #000;
+ background: var(--light-blue);
+}
+
+tbody tr {
+ border: 1px solid var(--dark-gray);
+}
+
+th, td {
+ padding: .15rem .6rem .15rem;
+ vertical-align: middle;
+}
+
+footer hr {
+ margin-top: 2rem;
+ margin-bottom: .4rem;
+}
+
+footer p {
+ font-size: 90%;
+ text-align: left;
+}
+
+hr {
+ border-top: .1rem solid #000;
+}
+
+blockquote {
+ width: 80%;
+ margin: 0 auto var(--parspace) auto;
+}
+
+cite {
+ display: block;
+ text-align: right
+}
+
+img {
+ display: block;
+ margin: auto;
+}
+
+div {
+ margin-bottom: var(--parspace);
+}
+
+ul {
+ margin: 0;
+ margin-block: 0 var(--parspace);
+ padding: 0;
+ padding-inline-start: 2em;
+}
diff --git a/themes/aoidh/hugo.toml b/themes/aoidh/hugo.toml
new file mode 100644
index 0000000..1410ef9
--- /dev/null
+++ b/themes/aoidh/hugo.toml
@@ -0,0 +1,4 @@
+[module]
+ [module.hugoVersion]
+ extended = false
+ min = '0.146.0'
diff --git a/themes/aoidh/layouts/_partials/footer.html b/themes/aoidh/layouts/_partials/footer.html
new file mode 100644
index 0000000..91d4a34
--- /dev/null
+++ b/themes/aoidh/layouts/_partials/footer.html
@@ -0,0 +1,17 @@
+<hr>
+<p>
+{{- with index (sort .Site.RegularPages "Date" "asc") 0 -}}
+ {{ $epoch := .Date }}
+ {{- with index (sort .Site.Pages "Lastmod" "desc") 0 -}}
+ {{ $lastMod := .Lastmod }}
+ {{- if gt $lastMod.Year $epoch.Year -}}
+ Copyright {{ $epoch.Year -}}&ndash;{{- $lastMod.Year -}}
+ {{- else -}}
+ Copyright {{ $epoch.Year -}}
+ {{- end -}}
+ {{- end -}}
+{{- end -}}
+{{- with .Site.Params.author.name -}}
+ , {{ . }}
+{{- end -}}
+. All rights reserved.</p>
diff --git a/themes/aoidh/layouts/_partials/head.html b/themes/aoidh/layouts/_partials/head.html
new file mode 100644
index 0000000..c72c8c5
--- /dev/null
+++ b/themes/aoidh/layouts/_partials/head.html
@@ -0,0 +1,6 @@
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width">
+<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ printf "%s | %s" .Title site.Title }}{{ end }}</title>
+{{- with resources.Get "aoidh.css" }}
+ <link rel="stylesheet" href="{{ .RelPermalink }}">
+{{- end }}
diff --git a/themes/aoidh/layouts/_partials/header.html b/themes/aoidh/layouts/_partials/header.html
new file mode 100644
index 0000000..8995308
--- /dev/null
+++ b/themes/aoidh/layouts/_partials/header.html
@@ -0,0 +1 @@
+{{ partial "menu.html" (dict "menuID" "main" "page" .) }}
diff --git a/themes/aoidh/layouts/_partials/menu.html b/themes/aoidh/layouts/_partials/menu.html
new file mode 100644
index 0000000..ba80749
--- /dev/null
+++ b/themes/aoidh/layouts/_partials/menu.html
@@ -0,0 +1,40 @@
+{{- /*
+Renders a menu for the given menu ID.
+
+@context {page} page The current page.
+@context {string} menuID The menu ID.
+
+@example: {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
+*/}}
+
+{{- $page := .page }}
+{{- $menuID := .menuID }}
+
+{{- with index site.Menus $menuID }}
+ <nav>
+ <ul>
+ {{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
+ </ul>
+ </nav>
+{{- end }}
+
+{{- define "_partials/inline/menu/walk.html" }}
+ {{- $page := .page }}
+ {{- range .menuEntries }}
+ <li
+ {{- if $page.IsMenuCurrent .Menu . -}}
+ {{- printf " class=%q" "nav-active" | safeHTMLAttr -}}
+ {{- else if $page.HasMenuCurrent .Menu . -}}
+ {{- printf " class=%q" "nav-ancestor" | safeHTMLAttr -}}
+ {{- end -}}>
+ <a
+ {{ printf "href=%q" .URL | safeHTMLAttr }}
+ >{{ .Name }}</a>
+ {{- with .Children }}
+ <ul>
+ {{- partial "inline/menu/walk.html" (dict "page" $page "menuEntries" .) }}
+ </ul>
+ {{- end }}
+ </li>
+ {{- end }}
+{{- end }}
diff --git a/themes/aoidh/layouts/_partials/terms.html b/themes/aoidh/layouts/_partials/terms.html
new file mode 100644
index 0000000..8a6ebec
--- /dev/null
+++ b/themes/aoidh/layouts/_partials/terms.html
@@ -0,0 +1,23 @@
+{{- /*
+For a given taxonomy, renders a list of terms assigned to the page.
+
+@context {page} page The current page.
+@context {string} taxonomy The taxonomy.
+
+@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
+*/}}
+
+{{- $page := .page }}
+{{- $taxonomy := .taxonomy }}
+
+{{- with $page.GetTerms $taxonomy }}
+ {{- $label := (index . 0).Parent.LinkTitle }}
+ <div>
+ <div>{{ $label }}:</div>
+ <ul>
+ {{- range . }}
+ <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
+ {{- end }}
+ </ul>
+ </div>
+{{- end }}
diff --git a/util.go b/util.go
deleted file mode 100644
index 5910a9f..0000000
--- a/util.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-Copyright 2022 Sam Anthony
-
-This file is part of samanthony.xyz.
-
-samanthony.xyz is free software: you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, either version 3 of the License, or (at your option) any later
-version.
-
-samanthony.xyz is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-samanthony.xyz. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-package main
-
-import (
- "bufio"
- "errors"
- "fmt"
- "golang.org/x/sys/unix"
- "log"
- "os"
- "runtime"
- "strconv"
- "strings"
-)
-
-func uidOf(user string) (int, error) {
- passwdFile, err := os.Open("/etc/passwd")
- if err != nil {
- return -1, err
- }
- defer passwdFile.Close()
-
- scanner := bufio.NewScanner(passwdFile)
- scanner.Split(bufio.ScanLines)
- for scanner.Scan() {
- line := scanner.Text()
-
- parsed := strings.Split(line, ":")
-
- name := parsed[0]
-
- if name == user {
- uid, err := strconv.Atoi(parsed[2])
- if err != nil {
- return -1, err
- }
- return uid, nil
- }
- }
- return -1, errors.New(fmt.Sprintf("user '%s' not in /etc/passwd", user))
-}
-
-func gidOf(group string) (int, error) {
- groupFile, err := os.Open("/etc/group")
- if err != nil {
- return -1, err
- }
- defer groupFile.Close()
-
- scanner := bufio.NewScanner(groupFile)
- scanner.Split(bufio.ScanLines)
- for scanner.Scan() {
- line := scanner.Text()
-
- parsed := strings.Split(line, ":")
-
- name := parsed[0]
-
- if name == group {
- gid, err := strconv.Atoi(parsed[2])
- if err != nil {
- return -1, err
- }
- return gid, nil
- }
- }
- return -1, errors.New(fmt.Sprintf("group '%s' not in /etc/group", group))
-}
-
-func dropPerms(uid, gid int) error {
- if runtime.GOOS != "linux" {
- if err := unix.Setgid(gid); err != nil {
- return errors.New(fmt.Sprintf("setgid(%d): %v", gid, err))
- }
- if err := unix.Setuid(uid); err != nil {
- return errors.New(fmt.Sprintf("setuid(%d): %v", uid, err))
- }
- return nil
- } else {
- // setuid/setgid has supposedly been fully supported on Linux
- // since go 1.16 but I can't seem to get it to work properly.
- log.Print("setgid not supported on Linux, skipping.")
- log.Print("setuid not supported on Linux, skipping.")
- return nil
- }
-}