shorten-share-url #2
14
Justfile
14
Justfile
@@ -6,8 +6,12 @@ user := "root"
|
||||
host := "goldfish.malzahn.lan"
|
||||
www-root := "/usr/share/caddy/public_html/fabula"
|
||||
|
||||
# Target architecture of the deploy host (override: `just go-arch=arm64 deploy-server`)
|
||||
go-arch := "amd64"
|
||||
|
||||
clean:
|
||||
rm -rf dist/*
|
||||
rm -f server/share-svc
|
||||
|
||||
serve:
|
||||
npm run dev
|
||||
@@ -18,6 +22,16 @@ build:
|
||||
deploy: build
|
||||
scp -r dist/* {{ user }}@{{ host }}:{{ www-root }}/
|
||||
|
||||
# Build the share service as a static binary for the deploy host.
|
||||
build-server:
|
||||
cd server && CGO_ENABLED=0 GOOS=linux GOARCH={{ go-arch }} go build -o share-svc .
|
||||
|
||||
# Ship the binary + unit file and (re)start the service. Requires root on host.
|
||||
deploy-server: build-server
|
||||
scp server/share-svc {{ user }}@{{ host }}:/usr/local/bin/share-svc
|
||||
scp server/share-svc.service {{ user }}@{{ host }}:/etc/systemd/system/share-svc.service
|
||||
ssh {{ user }}@{{ host }} 'systemctl daemon-reload && systemctl enable --now share-svc && systemctl restart share-svc'
|
||||
|
||||
format:
|
||||
npx prettier --write books/
|
||||
npx prettier --write src/
|
||||
|
||||
21
server/Caddyfile.snippet
Normal file
21
server/Caddyfile.snippet
Normal file
@@ -0,0 +1,21 @@
|
||||
# Caddy config for the share-link service.
|
||||
#
|
||||
# The frontend (served under /fabula/) calls /fabula/api/s, but the Go service
|
||||
# routes plain /api/s. `uri strip_prefix /fabula` rewrites /fabula/api/s ->
|
||||
# /api/s before proxying to the service on loopback.
|
||||
#
|
||||
# Place this INSIDE your existing site block, and make sure this /fabula/api/*
|
||||
# handler comes BEFORE the static handler that serves /fabula/* — `handle`
|
||||
# blocks match in source order, first match wins.
|
||||
|
||||
handle /fabula/api/* {
|
||||
uri strip_prefix /fabula
|
||||
reverse_proxy 127.0.0.1:8090
|
||||
}
|
||||
|
||||
# ... your existing static file handler for /fabula/* follows here, e.g.:
|
||||
#
|
||||
# handle /fabula/* {
|
||||
# root * /usr/share/caddy/public_html
|
||||
# file_server
|
||||
# }
|
||||
51
server/README.md
Normal file
51
server/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# share-svc
|
||||
|
||||
Tiny Go + SQLite service that stores character-sheet JSON blobs and returns
|
||||
short, content-addressed IDs, so sheets share as `...?s=<id>` instead of a giant
|
||||
inline `?c=` payload.
|
||||
|
||||
## API
|
||||
|
||||
| Method | Path | Body | Response |
|
||||
| ------ | ------------ | ----------- | ------------------- |
|
||||
| POST | `/api/s` | sheet JSON | `{"id":"<id>"}` |
|
||||
| GET | `/api/s/{id}`| – | sheet JSON |
|
||||
|
||||
IDs are `base64url(sha256(json))[:12]`, so identical sheets dedupe and re-sharing
|
||||
is idempotent. Payloads are stored deflated in a single SQLite file.
|
||||
|
||||
### Config (env)
|
||||
|
||||
| Var | Default | Notes |
|
||||
| ------------- | ------------- | ------------------------------------------------ |
|
||||
| `ADDR` | `:8090` | Listen address. The unit binds `127.0.0.1:8090`. |
|
||||
| `DB_PATH` | `shares.db` | SQLite file path. |
|
||||
| `CORS_ORIGIN` | _(unset)_ | Set to allow a cross-origin dev frontend. |
|
||||
|
||||
## Deploy
|
||||
|
||||
From the repo root:
|
||||
|
||||
```sh
|
||||
just deploy-server # build (linux/amd64) + ship + restart
|
||||
just go-arch=arm64 deploy-server # if the host is arm64
|
||||
```
|
||||
|
||||
That installs `/usr/local/bin/share-svc` and `share-svc.service`, then
|
||||
`daemon-reload` + `enable --now` + `restart`. The SQLite DB lives in
|
||||
`/var/lib/share-svc/` (created by the unit's `StateDirectory`).
|
||||
|
||||
### One-time Caddy setup
|
||||
|
||||
Add the `/fabula/api/*` reverse-proxy block from `Caddyfile.snippet` to your site
|
||||
block (before the static `/fabula/*` handler) and reload Caddy.
|
||||
|
||||
## Local dev
|
||||
|
||||
`webpack serve` (port 8080) does not serve `/api/*`, so `?s=` sharing falls back
|
||||
to inline `?c=` links. To exercise short links locally, run the service and point
|
||||
the frontend at it:
|
||||
|
||||
```sh
|
||||
CORS_ORIGIN=http://localhost:8080 go run ./server
|
||||
```
|
||||
35
server/share-svc.service
Normal file
35
server/share-svc.service
Normal file
@@ -0,0 +1,35 @@
|
||||
[Unit]
|
||||
Description=Fabula Ultima share-link service
|
||||
Documentation=https://git.illaoi.pro/drew/fabula-ultima-html
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/share-svc
|
||||
# Bind to loopback only; Caddy reverse-proxies public traffic to it.
|
||||
Environment=ADDR=127.0.0.1:8090
|
||||
Environment=DB_PATH=/var/lib/share-svc/shares.db
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
|
||||
# Run as an ephemeral, unprivileged user. StateDirectory creates and chowns
|
||||
# /var/lib/share-svc so the SQLite file persists across restarts.
|
||||
DynamicUser=yes
|
||||
StateDirectory=share-svc
|
||||
|
||||
# Hardening
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6
|
||||
RestrictNamespaces=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user