mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 19:34:03 +01:00
setup_syncyomi.sh 408 -> 138 (two cuts), setup_personal_website.sh 396 -> 192, fix_thorium.sh 377 -> 208. All three already declared SCRIPT_DIR and the splitter added a second one, which aborts at runtime with "readonly variable" while bash -n and shellcheck stay clean. The stubbed run caught it; the splitter now reuses an existing declaration. Fixed SC2164 in build_website rather than suppressing it: a bare cd "$WEBSITE_SRC" inside a subshell, with pnpm install and pnpm run build after it. A failed cd would have built in the caller's directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
214 lines
6.3 KiB
Bash
Executable File
214 lines
6.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# CV build, website generation and the Caddy stack snippets.
|
|
#
|
|
# Sourced by setup_personal_website.sh; split out to keep it under the 250-line cap.
|
|
# Sourced rather than run, so it inherits the caller's strict mode and
|
|
# the helper functions and variables defined above the source line.
|
|
|
|
# --- Phase 5: build the website --------------------------------------------
|
|
build_website() {
|
|
log_info "Installing website dependencies and building…"
|
|
(
|
|
cd "$WEBSITE_SRC" || die "Cannot enter ${WEBSITE_SRC}."
|
|
pnpm install --frozen-lockfile
|
|
pnpm run build
|
|
)
|
|
[[ -f "${WEBSITE_SRC}/dist/index.html" ]] ||
|
|
die "Build did not produce ${WEBSITE_SRC}/dist/index.html."
|
|
log_ok "Website built to ${WEBSITE_SRC}/dist."
|
|
}
|
|
|
|
# --- Phase 6: write the website's static-serve stack ------------------------
|
|
write_website_stack() {
|
|
ensure_dir "$WEBSITE_DATA_DIR"
|
|
cat >"$WEBSITE_COMPOSE" <<EOF
|
|
# Managed by setup_personal_website.sh — do not edit by hand.
|
|
services:
|
|
personal-website:
|
|
image: caddy:2.8
|
|
container_name: personal-website
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
volumes:
|
|
- ${WEBSITE_SRC}/dist:/srv:ro
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
EOF
|
|
|
|
cat >"$WEBSITE_INNER_CADDY" <<EOF
|
|
# Managed by setup_personal_website.sh — do not edit by hand.
|
|
# Static file server on loopback; fronted by gitea-caddy on the public domain.
|
|
#
|
|
# admin off is REQUIRED: this container shares the host network namespace with
|
|
# gitea-caddy, so without it both Caddies would listen on the admin API (:2019)
|
|
# and a 'caddy reload' aimed at gitea-caddy could be applied to this one instead
|
|
# — which would make it grab 80/443 and run ACME. Disabling admin keeps this
|
|
# process strictly a loopback static server on ${WEBSITE_PORT}.
|
|
{
|
|
admin off
|
|
}
|
|
|
|
:${WEBSITE_PORT} {
|
|
root * /srv
|
|
try_files {path} /index.html
|
|
file_server
|
|
encode gzip
|
|
}
|
|
EOF
|
|
log_ok "Wrote ${WEBSITE_COMPOSE} and ${WEBSITE_INNER_CADDY}."
|
|
}
|
|
|
|
# --- Phase 7: front the website on the root domain --------------------------
|
|
write_website_snippet() {
|
|
cat >"$WEBSITE_SNIPPET" <<EOF
|
|
# Managed by setup_personal_website.sh — do not edit by hand.
|
|
${WEBSITE_DOMAIN} {
|
|
reverse_proxy 127.0.0.1:${WEBSITE_PORT}
|
|
}
|
|
EOF
|
|
log_ok "Wrote ${WEBSITE_SNIPPET}."
|
|
}
|
|
|
|
# --- Phase 8: start the website container -----------------------------------
|
|
wait_for_website() {
|
|
local _
|
|
for _ in $(seq 1 30); do
|
|
if curl -sf -o /dev/null "http://127.0.0.1:${WEBSITE_PORT}/"; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
start_website() {
|
|
log_info "Starting the personal-website container…"
|
|
docker compose -f "$WEBSITE_COMPOSE" up -d
|
|
if wait_for_website; then
|
|
log_ok "Website answering on http://127.0.0.1:${WEBSITE_PORT}/."
|
|
else
|
|
die "Website did not become reachable on 127.0.0.1:${WEBSITE_PORT}."
|
|
fi
|
|
}
|
|
|
|
# --- Phase 9: reload the edge -----------------------------------------------
|
|
reload_caddy() {
|
|
if docker exec "$CADDY_CONTAINER" caddy validate --config /etc/caddy/Caddyfile >/dev/null 2>&1; then
|
|
docker exec "$CADDY_CONTAINER" caddy reload --config /etc/caddy/Caddyfile
|
|
log_ok "Reloaded ${CADDY_CONTAINER}."
|
|
else
|
|
die "Caddy config invalid — aborting reload. Check ${SITES_DIR}/*.caddy."
|
|
fi
|
|
}
|
|
|
|
# --- Phase 10: firewall -----------------------------------------------------
|
|
ensure_firewall() {
|
|
log_info "Ensuring ports 80/443 are open (idempotent)…"
|
|
sudo bash "$WG_SCRIPT" allow-web
|
|
}
|
|
|
|
# --- Phase 11: report -------------------------------------------------------
|
|
print_report() {
|
|
cat <<EOF
|
|
|
|
============================================================================
|
|
Personal website deployed.
|
|
============================================================================
|
|
Website : https://${WEBSITE_DOMAIN}/
|
|
Gitea : https://${GITEA_DOMAIN}/ (moved from the root)
|
|
|
|
Gitea moved to a subdomain. Re-point any local clones/mirrors:
|
|
git remote set-url <name> https://${GITEA_DOMAIN}/<owner>/<repo>.git
|
|
|
|
Acceptance test (do this on your phone):
|
|
Turn Wi-Fi OFF (use cellular) and open https://${WEBSITE_DOMAIN}/ —
|
|
it should load over HTTPS with the hero, live GitHub projects, and CV,
|
|
and be usable at phone width.
|
|
============================================================================
|
|
EOF
|
|
}
|
|
|
|
setup_cmd() {
|
|
print_setup_header "Personal website setup"
|
|
preflight
|
|
migrate_caddy_snippets
|
|
relocate_gitea
|
|
build_cv_json
|
|
build_website
|
|
write_website_stack
|
|
write_website_snippet
|
|
start_website
|
|
reload_caddy
|
|
ensure_firewall
|
|
print_report
|
|
log_ok "Personal website setup complete."
|
|
}
|
|
|
|
# --- status: self-diagnose --------------------------------------------------
|
|
status_line() {
|
|
if [[ $1 -eq 0 ]]; then
|
|
log_ok "$2"
|
|
else
|
|
log_warn "$2"
|
|
fi
|
|
}
|
|
|
|
check_http() {
|
|
# Prints 0 to stdout if the URL returns 200, else 1.
|
|
local url="$1" code
|
|
code="$(curl -sk -o /dev/null -w '%{http_code}' --max-time 10 "$url" 2>/dev/null || echo 000)"
|
|
[[ $code == "200" ]] && echo 0 || echo 1
|
|
}
|
|
|
|
status_cmd() {
|
|
print_setup_header "Personal website status"
|
|
|
|
if has_cmd node; then
|
|
status_line 0 "node present"
|
|
else
|
|
status_line 1 "node missing"
|
|
fi
|
|
if has_cmd pnpm; then
|
|
status_line 0 "pnpm present"
|
|
else
|
|
status_line 1 "pnpm missing"
|
|
fi
|
|
if [[ -f "${WEBSITE_SRC}/dist/index.html" ]]; then
|
|
status_line 0 "build present (dist/index.html)"
|
|
else
|
|
status_line 1 "build missing — run setup"
|
|
fi
|
|
if docker ps --format '{{.Names}}' | grep -qx personal-website; then
|
|
status_line 0 "personal-website container running"
|
|
else
|
|
status_line 1 "personal-website container not running"
|
|
fi
|
|
|
|
status_line "$(check_http "http://127.0.0.1:${WEBSITE_PORT}/")" \
|
|
"local static server (127.0.0.1:${WEBSITE_PORT})"
|
|
|
|
if [[ -f $WEBSITE_SNIPPET ]]; then
|
|
status_line 0 "Caddy website snippet present"
|
|
else
|
|
status_line 1 "Caddy website snippet missing"
|
|
fi
|
|
if grep -qE '^[[:space:]]*import[[:space:]]+/etc/caddy/sites' "$CADDYFILE" 2>/dev/null; then
|
|
status_line 0 "root Caddyfile uses snippet imports"
|
|
else
|
|
status_line 1 "root Caddyfile not migrated to imports"
|
|
fi
|
|
if getent hosts "$WEBSITE_DOMAIN" >/dev/null; then
|
|
status_line 0 "${WEBSITE_DOMAIN} resolves"
|
|
else
|
|
status_line 1 "${WEBSITE_DOMAIN} does not resolve"
|
|
fi
|
|
if getent hosts "$GITEA_DOMAIN" >/dev/null; then
|
|
status_line 0 "${GITEA_DOMAIN} resolves"
|
|
else
|
|
status_line 1 "${GITEA_DOMAIN} does not resolve"
|
|
fi
|
|
status_line "$(check_http "https://${WEBSITE_DOMAIN}/")" \
|
|
"external https://${WEBSITE_DOMAIN}/"
|
|
status_line "$(check_http "https://${GITEA_DOMAIN}/")" \
|
|
"external https://${GITEA_DOMAIN}/"
|
|
}
|