Rename: Hamlet -> Ham Lab (binary/module/service/config hamlab)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
436732dac0
commit
d76da4b37a
@@ -10,11 +10,11 @@ web:
|
|||||||
cd web && npm ci --no-audit --no-fund && npm run build
|
cd web && npm ci --no-audit --no-fund && npm run build
|
||||||
|
|
||||||
build: web
|
build: web
|
||||||
$(GO) build -trimpath -ldflags "-s -w -X main.version=$(VERSION)" -o bin/hamlet ./cmd/hamlet
|
$(GO) build -trimpath -ldflags "-s -w -X main.version=$(VERSION)" -o bin/hamlab ./cmd/hamlab
|
||||||
|
|
||||||
# backend only (assumes web/dist already built)
|
# backend only (assumes web/dist already built)
|
||||||
go:
|
go:
|
||||||
$(GO) build -ldflags "-X main.version=$(VERSION)" -o bin/hamlet ./cmd/hamlet
|
$(GO) build -ldflags "-X main.version=$(VERSION)" -o bin/hamlab ./cmd/hamlab
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
cd web && npm run dev
|
cd web && npm run dev
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Hamlet — MVP plan
|
# Ham Lab — MVP plan
|
||||||
|
|
||||||
Proxmox-style web console for a bare-metal Debian 13 host. First target: `core`
|
Proxmox-style web console for a bare-metal Debian 13 host. First target: `core`
|
||||||
(10.0.0.234). MVP is **service + shell + persistent terminals**; everything else is a
|
(10.0.0.234). MVP is **service + shell + persistent terminals**; everything else is a
|
||||||
@@ -13,17 +13,17 @@ stub tab or a roadmap item.
|
|||||||
| Transport | One WebSocket per attached terminal; REST for everything else | Standard; NPM passes WebSockets with one toggle. |
|
| Transport | One WebSocket per attached terminal; REST for everything else | Standard; NPM passes WebSockets with one toggle. |
|
||||||
| Frontend | **React + TypeScript + Vite**, `xterm.js` (+ fit, webgl, web-links addons) | Matches `calorie-counter` / `wedding-website`, so no new toolchain to learn. |
|
| Frontend | **React + TypeScript + Vite**, `xterm.js` (+ fit, webgl, web-links addons) | Matches `calorie-counter` / `wedding-website`, so no new toolchain to learn. |
|
||||||
| Auth | Single-user **password login**, argon2id hash, HttpOnly/Secure/SameSite=Strict session cookie, login rate-limit, Origin check on WS upgrade | This is a root-capable shell on the public internet. Auth is MVP, not roadmap. |
|
| Auth | Single-user **password login**, argon2id hash, HttpOnly/Secure/SameSite=Strict session cookie, login rate-limit, Origin check on WS upgrade | This is a root-capable shell on the public internet. Auth is MVP, not roadmap. |
|
||||||
| Service | `hamlet.service`, `User=core`, `Restart=always`, binary at `/usr/local/bin/hamlet` | Same pattern as `calorie-counter.service`. Runs as `core` so `sudo` works in the terminal without a password, as on the SSH path. |
|
| Service | `hamlab.service`, `User=core`, `Restart=always`, binary at `/usr/local/bin/hamlab` | Same pattern as `calorie-counter.service`. Runs as `core` so `sudo` works in the terminal without a password, as on the SSH path. |
|
||||||
| Listen | `10.0.0.234:8100` (free) | NPM is a container, so loopback-only won't do. Firewalling to NPM is a later hardening step. |
|
| Listen | `10.0.0.234:8100` (free) | NPM is a container, so loopback-only won't do. Firewalling to NPM is a later hardening step. |
|
||||||
| Data | `/home/core/apps/hamlet/` — `config.yaml`, `state.db` (SQLite) | `apps/` is the nightly rsync target to `grid`, so it's backed up for free. |
|
| Data | `/home/core/apps/hamlab/` — `config.yaml`, `state.db` (SQLite) | `apps/` is the nightly rsync target to `grid`, so it's backed up for free. |
|
||||||
| Ingress | NPM proxy host `hamlet.bigham.dev` → `:8100`, LE cert, WebSockets on, caching off, force-SSL + HSTS + HTTP/2 set in a **second** API call (NPM drops them on create-with-new-cert) | `*.bigham.dev` already resolves; the proxy host alone makes it live. |
|
| Ingress | NPM proxy host `hamlab.bigham.dev` → `:8100`, LE cert, WebSockets on, caching off, force-SSL + HSTS + HTTP/2 set in a **second** API call (NPM drops them on create-with-new-cert) | `*.bigham.dev` already resolves; the proxy host alone makes it live. |
|
||||||
| Repo | Gitea `mitchell/hamlet`, private, `main` | Created 2026-09-17. |
|
| Repo | Gitea `mitchell/hamlab`, private, `main` | Created 2026-09-17. |
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ header: Hamlet · core · load 0.42 · ● connected · [logout] │
|
│ header: Ham Lab · core · load 0.42 · ● connected · [logout] │
|
||||||
├──────────────┬───────────────────────────────────────────────┤
|
├──────────────┬───────────────────────────────────────────────┤
|
||||||
│ ▾ Terminal │ │
|
│ ▾ Terminal │ │
|
||||||
│ shell 1 ● │ │
|
│ shell 1 ● │ │
|
||||||
@@ -34,7 +34,7 @@ stub tab or a roadmap item.
|
|||||||
│ ▸ ZFS │ │
|
│ ▸ ZFS │ │
|
||||||
│ ▸ Dashboard │ │
|
│ ▸ Dashboard │ │
|
||||||
├──────────────┴───────────────────────────────────────────────┤
|
├──────────────┴───────────────────────────────────────────────┤
|
||||||
│ footer: v0.1.0 · up 12d · hamlet.bigham.dev │
|
│ footer: v0.1.0 · up 12d · hamlab.bigham.dev │
|
||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ Everything under `/api` except `login` and `/healthz` requires the session cooki
|
|||||||
## Project layout
|
## Project layout
|
||||||
|
|
||||||
```
|
```
|
||||||
hamlet/
|
hamlab/
|
||||||
cmd/hamlet/main.go serve | passwd | version
|
cmd/hamlab/main.go serve | passwd | version
|
||||||
internal/
|
internal/
|
||||||
auth/ argon2id, sessions, rate limit
|
auth/ argon2id, sessions, rate limit
|
||||||
term/ PTY manager, ring buffer, attach/detach
|
term/ PTY manager, ring buffer, attach/detach
|
||||||
@@ -90,16 +90,16 @@ hamlet/
|
|||||||
web/ go:embed of web/dist
|
web/ go:embed of web/dist
|
||||||
web/ Vite + React + TS + xterm.js, PWA manifest + SW
|
web/ Vite + React + TS + xterm.js, PWA manifest + SW
|
||||||
deploy/
|
deploy/
|
||||||
hamlet.service
|
hamlab.service
|
||||||
install.sh build → /usr/local/bin/hamlet → systemctl restart
|
install.sh build → /usr/local/bin/hamlab → systemctl restart
|
||||||
Makefile build / dev / deploy / lint / test
|
Makefile build / dev / deploy / lint / test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Milestones
|
## Milestones
|
||||||
|
|
||||||
**M0 — skeleton** (service boots, login works)
|
**M0 — skeleton** (service boots, login works)
|
||||||
- Go module, chi, config loading, `hamlet passwd`, argon2id login, session cookie.
|
- Go module, chi, config loading, `hamlab passwd`, argon2id login, session cookie.
|
||||||
- `hamlet.service` on `core`, `:8100`, serves a placeholder page.
|
- `hamlab.service` on `core`, `:8100`, serves a placeholder page.
|
||||||
- `make deploy` from `core` itself.
|
- `make deploy` from `core` itself.
|
||||||
|
|
||||||
**M1 — terminals** (the MVP feature)
|
**M1 — terminals** (the MVP feature)
|
||||||
@@ -111,7 +111,7 @@ hamlet/
|
|||||||
- Mobile drawer, key bar, fit-on-rotate.
|
- Mobile drawer, key bar, fit-on-rotate.
|
||||||
- `manifest.webmanifest`, icons (Ham logo on black, same recipe as HamTV), service
|
- `manifest.webmanifest`, icons (Ham logo on black, same recipe as HamTV), service
|
||||||
worker caching the app shell only (never `/api`).
|
worker caching the app shell only (never `/api`).
|
||||||
- NPM proxy host for `hamlet.bigham.dev`; verify WS through NPM; force-SSL/HSTS/HTTP2.
|
- NPM proxy host for `hamlab.bigham.dev`; verify WS through NPM; force-SSL/HSTS/HTTP2.
|
||||||
- Stub groups for Services / Cron / ZFS / Dashboard.
|
- Stub groups for Services / Cron / ZFS / Dashboard.
|
||||||
|
|
||||||
**Done when:** from the phone, open the PWA, log in, open two terminals, run
|
**Done when:** from the phone, open the PWA, log in, open two terminals, run
|
||||||
@@ -120,7 +120,7 @@ something long in one, switch away and back, refresh — it's all still there.
|
|||||||
## Roadmap (not MVP — notes only)
|
## Roadmap (not MVP — notes only)
|
||||||
|
|
||||||
- **tmux-backed sessions** so shells survive a daemon restart / redeploy. Would spawn
|
- **tmux-backed sessions** so shells survive a daemon restart / redeploy. Would spawn
|
||||||
`tmux new -A -s hamlet-<id>`; needs `mouse on` and status-bar handling in xterm.
|
`tmux new -A -s hamlab-<id>`; needs `mouse on` and status-bar handling in xterm.
|
||||||
- **Services tab** — `systemctl list-units`, per-unit page: status, last 200 journal
|
- **Services tab** — `systemctl list-units`, per-unit page: status, last 200 journal
|
||||||
lines (live-tail via WS), start/stop/restart/enable buttons, unit file viewer.
|
lines (live-tail via WS), start/stop/restart/enable buttons, unit file viewer.
|
||||||
- **Cron tab** — parse `/etc/crontab`, `/etc/cron.d/*`, user crontabs, and systemd
|
- **Cron tab** — parse `/etc/crontab`, `/etc/cron.d/*`, user crontabs, and systemd
|
||||||
@@ -129,7 +129,7 @@ something long in one, switch away and back, refresh — it's all still there.
|
|||||||
scrub status, and the `freeing` counter. `grid` is the target, which implies
|
scrub status, and the `freeing` counter. `grid` is the target, which implies
|
||||||
multi-host.
|
multi-host.
|
||||||
- **Dashboard** — CPU/mem/load/net, disks and SMART (`smartctl -j`), temps and fans
|
- **Dashboard** — CPU/mem/load/net, disks and SMART (`smartctl -j`), temps and fans
|
||||||
(`sensors -j`), UPS from `pwrstat`. Hamlet collects on a timer into a SQLite
|
(`sensors -j`), UPS from `pwrstat`. Ham Lab collects on a timer into a SQLite
|
||||||
ring (e.g. 10 s resolution, 7 days) and draws sparklines. This is where the
|
ring (e.g. 10 s resolution, 7 days) and draws sparklines. This is where the
|
||||||
"collects its own data" requirement lands; no Prometheus dependency.
|
"collects its own data" requirement lands; no Prometheus dependency.
|
||||||
- **Multi-host** — same binary as a headless *agent* on `grid` / Mac mini /
|
- **Multi-host** — same binary as a headless *agent* on `grid` / Mac mini /
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Hamlet
|
# Ham Lab
|
||||||
|
|
||||||
Web-based Linux management console for bare-metal Debian, in the style of the Proxmox
|
Web-based Linux management console for bare-metal Debian, in the style of the Proxmox
|
||||||
web UI. Named after Ham the cat (see also HamTV), and the play.
|
web UI. Named after Ham the cat (see also HamTV).
|
||||||
|
|
||||||
Status: **planning**. See [`PLAN.md`](PLAN.md) for the MVP scope and roadmap. No code yet.
|
Status: **planning**. See [`PLAN.md`](PLAN.md) for the MVP scope and roadmap. No code yet.
|
||||||
|
|
||||||
- Runs as a systemd service on the host it manages, as user `core`.
|
- Runs as a systemd service on the host it manages, as user `core`.
|
||||||
- Public at `https://hamlet.bigham.dev` (behind Nginx Proxy Manager on `core`).
|
- Public at `https://hamlab.bigham.dev` (behind Nginx Proxy Manager on `core`).
|
||||||
- Installable as a PWA.
|
- Installable as a PWA.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Hamlet — Proxmox-style web console for a bare-metal Debian host.
|
// Ham Lab — Proxmox-style web console for a bare-metal Debian host.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -17,25 +17,25 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
"hamlet/internal/api"
|
"hamlab/internal/api"
|
||||||
"hamlet/internal/auth"
|
"hamlab/internal/auth"
|
||||||
"hamlet/internal/config"
|
"hamlab/internal/config"
|
||||||
"hamlet/internal/store"
|
"hamlab/internal/store"
|
||||||
hterm "hamlet/internal/term"
|
hterm "hamlab/internal/term"
|
||||||
"hamlet/internal/web"
|
"hamlab/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defaultCfg := os.Getenv("HAMLET_CONFIG")
|
defaultCfg := os.Getenv("HAMLAB_CONFIG")
|
||||||
if defaultCfg == "" {
|
if defaultCfg == "" {
|
||||||
home, _ := os.UserHomeDir()
|
home, _ := os.UserHomeDir()
|
||||||
defaultCfg = filepath.Join(home, "apps", "hamlet", "config.json")
|
defaultCfg = filepath.Join(home, "apps", "hamlab", "config.json")
|
||||||
}
|
}
|
||||||
cfgPath := flag.String("config", defaultCfg, "path to config.json")
|
cfgPath := flag.String("config", defaultCfg, "path to config.json")
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(os.Stderr, "usage: hamlet [-config path] [serve|passwd|version]\n")
|
fmt.Fprintf(os.Stderr, "usage: hamlab [-config path] [serve|passwd|version]\n")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@@ -50,7 +50,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "version":
|
case "version":
|
||||||
fmt.Println("hamlet", version)
|
fmt.Println("hamlab", version)
|
||||||
case "passwd":
|
case "passwd":
|
||||||
if err := passwd(cfg); err != nil {
|
if err := passwd(cfg); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -98,7 +98,7 @@ func passwd(cfg *config.Config) error {
|
|||||||
|
|
||||||
func serve(cfg *config.Config) error {
|
func serve(cfg *config.Config) error {
|
||||||
if cfg.PasswordHash == "" {
|
if cfg.PasswordHash == "" {
|
||||||
return fmt.Errorf("no password set — run: hamlet -config %s passwd", cfg.Path())
|
return fmt.Errorf("no password set — run: hamlab -config %s passwd", cfg.Path())
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(cfg.DataDir(), 0o700); err != nil {
|
if err := os.MkdirAll(cfg.DataDir(), 0o700); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -152,7 +152,7 @@ func serve(cfg *config.Config) error {
|
|||||||
_ = hs.Shutdown(ctx)
|
_ = hs.Shutdown(ctx)
|
||||||
mgr.Shutdown()
|
mgr.Shutdown()
|
||||||
}()
|
}()
|
||||||
log.Printf("hamlet %s listening on %s as %s (shell %s)", version, cfg.Listen, u.Username, shell)
|
log.Printf("hamlab %s listening on %s as %s (shell %s)", version, cfg.Listen, u.Username, shell)
|
||||||
if err := hs.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
if err := hs.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Hamlet web console
|
Description=Ham Lab web console
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
@@ -7,8 +7,8 @@ Type=simple
|
|||||||
User=core
|
User=core
|
||||||
Group=core
|
Group=core
|
||||||
WorkingDirectory=/home/core
|
WorkingDirectory=/home/core
|
||||||
Environment=HAMLET_CONFIG=/home/core/apps/hamlet/config.json
|
Environment=HAMLAB_CONFIG=/home/core/apps/hamlab/config.json
|
||||||
ExecStart=/usr/local/bin/hamlet serve
|
ExecStart=/usr/local/bin/hamlab serve
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=3
|
RestartSec=3
|
||||||
KillMode=control-group
|
KillMode=control-group
|
||||||
+5
-5
@@ -2,10 +2,10 @@
|
|||||||
# Install the built binary and unit on this host, then (re)start the service.
|
# Install the built binary and unit on this host, then (re)start the service.
|
||||||
set -eu
|
set -eu
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
sudo install -m 0755 bin/hamlet /usr/local/bin/hamlet
|
sudo install -m 0755 bin/hamlab /usr/local/bin/hamlab
|
||||||
sudo install -m 0644 deploy/hamlet.service /etc/systemd/system/hamlet.service
|
sudo install -m 0644 deploy/hamlab.service /etc/systemd/system/hamlab.service
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable --now hamlet.service >/dev/null
|
sudo systemctl enable --now hamlab.service >/dev/null
|
||||||
sudo systemctl restart hamlet.service
|
sudo systemctl restart hamlab.service
|
||||||
sleep 1
|
sleep 1
|
||||||
systemctl is-active hamlet.service && /usr/local/bin/hamlet version
|
systemctl is-active hamlab.service && /usr/local/bin/hamlab version
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import (
|
|||||||
|
|
||||||
"github.com/coder/websocket"
|
"github.com/coder/websocket"
|
||||||
|
|
||||||
"hamlet/internal/auth"
|
"hamlab/internal/auth"
|
||||||
"hamlet/internal/config"
|
"hamlab/internal/config"
|
||||||
"hamlet/internal/term"
|
"hamlab/internal/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
const cookieName = "hamlet_session"
|
const cookieName = "hamlab_session"
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/argon2"
|
"golang.org/x/crypto/argon2"
|
||||||
|
|
||||||
"hamlet/internal/store"
|
"hamlab/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Package config loads and saves hamlet's JSON config file.
|
// Package config loads and saves hamlab's JSON config file.
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (m *Manager) Create(id, name string) (*Session, error) {
|
|||||||
}
|
}
|
||||||
cmd := exec.Command(m.shell, "-l")
|
cmd := exec.Command(m.shell, "-l")
|
||||||
cmd.Dir = m.home
|
cmd.Dir = m.home
|
||||||
cmd.Env = append(os.Environ(), "TERM=xterm-256color", "COLORTERM=truecolor", "HAMLET=1")
|
cmd.Env = append(os.Environ(), "TERM=xterm-256color", "COLORTERM=truecolor", "HAMLAB=1")
|
||||||
ptmx, err := pty.StartWithSize(cmd, &pty.Winsize{Cols: defaultCol, Rows: defaultRow})
|
ptmx, err := pty.StartWithSize(cmd, &pty.Winsize{Cols: defaultCol, Rows: defaultRow})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+2
-2
@@ -7,11 +7,11 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Hamlet" />
|
<meta name="apple-mobile-web-app-title" content="Ham Lab" />
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" href="/icons/icon-192.png" />
|
<link rel="icon" href="/icons/icon-192.png" />
|
||||||
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
|
||||||
<title>Hamlet</title>
|
<title>Ham Lab</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "hamlet-web",
|
"name": "hamlab-web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hamlet-web",
|
"name": "hamlab-web",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "hamlet-web",
|
"name": "hamlab-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Hamlet",
|
"name": "Ham Lab",
|
||||||
"short_name": "Hamlet",
|
"short_name": "Ham Lab",
|
||||||
"description": "Linux management console",
|
"description": "Linux management console",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"scope": "/",
|
"scope": "/",
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
// Hamlet service worker: cache hashed assets, never the API, network-first for the shell.
|
// Ham Lab service worker: cache hashed assets, never the API, network-first for the shell.
|
||||||
const CACHE = 'hamlet-v1';
|
const CACHE = 'hamlab-v1';
|
||||||
self.addEventListener('install', () => self.skipWaiting());
|
self.addEventListener('install', () => self.skipWaiting());
|
||||||
self.addEventListener('activate', (e) => {
|
self.addEventListener('activate', (e) => {
|
||||||
e.waitUntil(caches.keys().then((ks) => Promise.all(ks.filter((k) => k !== CACHE).map((k) => caches.delete(k)))).then(() => self.clients.claim()));
|
e.waitUntil(caches.keys().then((ks) => Promise.all(ks.filter((k) => k !== CACHE).map((k) => caches.delete(k)))).then(() => self.clients.claim()));
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ export default function Login({ onLogin }: { onLogin: () => void }) {
|
|||||||
return (
|
return (
|
||||||
<div className="center">
|
<div className="center">
|
||||||
<form className="login" onSubmit={submit}>
|
<form className="login" onSubmit={submit}>
|
||||||
<h1>Hamlet</h1>
|
<h1>Ham Lab</h1>
|
||||||
<input type="password" autoFocus autoComplete="current-password" placeholder="password"
|
<input type="password" autoFocus autoComplete="current-password" placeholder="password"
|
||||||
value={pw} onChange={(e) => setPw(e.target.value)} />
|
value={pw} onChange={(e) => setPw(e.target.value)} />
|
||||||
<button disabled={busy || !pw}>{busy ? '…' : 'Sign in'}</button>
|
<button disabled={busy || !pw}>{busy ? '…' : 'Sign in'}</button>
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ export default function Shell({ me, onLoggedOut }: { me: Me; onLoggedOut: () =>
|
|||||||
<div className={'app' + (drawer ? ' drawer-open' : '')}>
|
<div className={'app' + (drawer ? ' drawer-open' : '')}>
|
||||||
<header className="header">
|
<header className="header">
|
||||||
<button className="icon-btn menu" onClick={() => setDrawer(!drawer)} aria-label="menu">☰</button>
|
<button className="icon-btn menu" onClick={() => setDrawer(!drawer)} aria-label="menu">☰</button>
|
||||||
<span className="brand">Hamlet</span>
|
<span className="brand">Ham Lab</span>
|
||||||
<span className="host">{me.user}@{me.host}</span>
|
<span className="host">{me.user}@{me.host}</span>
|
||||||
<span className="spacer" />
|
<span className="spacer" />
|
||||||
<span className="stat" title="load average">{me.load}</span>
|
<span className="stat" title="load average">{me.load}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user