mirror of
https://github.com/cole-maxwell1/todo-hetzner.git
synced 2026-10-27 19:02:49 -05:00
feat: add Hetzner-hosted todo application stack
Implement the server-rendered Go todo application with PostgreSQL persistence, Tailwind styling, health checks, and test coverage. Provision production and staging infrastructure with OpenTofu, including private networking, static IPs, Object Storage backups, and load balancing. Configure Rocky Linux hosts with Ansible, rootless Podman, PostgreSQL, WAL-G backup automation, staging restores, and generated inventory.
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
package web
|
||||
|
||||
import "embed"
|
||||
|
||||
// Assets contains server-rendered templates and compiled static files.
|
||||
//
|
||||
//go:embed templates static
|
||||
var Assets embed.FS
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
||||
{{define "layout"}}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Field Notes</title>
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
</head>
|
||||
<body class="min-h-screen bg-stone-950 text-stone-100 antialiased">
|
||||
<main class="mx-auto max-w-3xl px-5 py-12 sm:px-8 sm:py-20">
|
||||
{{template "content" .}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
@@ -0,0 +1,37 @@
|
||||
{{define "content"}}
|
||||
<header class="mb-12 border-l-4 border-amber-400 pl-5">
|
||||
<p class="mb-2 font-mono text-xs uppercase tracking-[0.3em] text-amber-400">Daily ledger</p>
|
||||
<h1 class="text-4xl font-black tracking-tight sm:text-6xl">Field Notes</h1>
|
||||
<p class="mt-3 text-stone-400">Small commitments, made concrete.</p>
|
||||
</header>
|
||||
|
||||
<form action="/todos" method="post" class="mb-10 flex flex-col gap-3 sm:flex-row">
|
||||
<label for="title" class="sr-only">New task</label>
|
||||
<input id="title" name="title" required maxlength="200" autocomplete="off" placeholder="What needs doing?" class="min-w-0 flex-1 rounded-none border border-stone-700 bg-stone-900 px-4 py-3 text-base outline-none transition focus:border-amber-400 focus:ring-1 focus:ring-amber-400">
|
||||
<button class="bg-amber-400 px-6 py-3 font-bold text-stone-950 transition hover:bg-amber-300 focus:outline-none focus:ring-2 focus:ring-amber-200">Add note</button>
|
||||
</form>
|
||||
|
||||
<section aria-label="Tasks">
|
||||
{{if .Todos}}
|
||||
<ul class="divide-y divide-stone-800 border-y border-stone-800">
|
||||
{{range .Todos}}
|
||||
<li class="group flex items-center gap-4 py-5">
|
||||
<form action="/todos/{{.ID}}/toggle" method="post">
|
||||
<button aria-label="Toggle {{.Title}}" class="flex h-7 w-7 items-center justify-center border-2 border-stone-600 text-stone-950 transition hover:border-amber-400 {{if .Completed}}border-amber-400 bg-amber-400{{end}}">
|
||||
{{if .Completed}}<span aria-hidden="true">✓</span>{{end}}
|
||||
</button>
|
||||
</form>
|
||||
<span class="min-w-0 flex-1 break-words text-lg {{if .Completed}}text-stone-500 line-through{{end}}">{{.Title}}</span>
|
||||
<form action="/todos/{{.ID}}/delete" method="post">
|
||||
<button class="px-2 py-1 font-mono text-xs uppercase tracking-widest text-stone-500 transition hover:text-red-400" aria-label="Delete {{.Title}}">Delete</button>
|
||||
</form>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<div class="border border-dashed border-stone-700 px-6 py-14 text-center text-stone-500">
|
||||
<p class="font-mono text-sm uppercase tracking-widest">The page is clear</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user