Skip to content

Commit 7e672d0

Browse files
Fix local dev dashboard UI build when dist dirs are root-owned.
Sync npm output from the fallback .dev/vite-dist path into go:embed, restore dist permissions via make fix-dev-permissions, and isolate dev secrets under .dev/. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b688e82 commit 7e672d0

10 files changed

Lines changed: 491 additions & 49 deletions

‎Makefile‎

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# TrinityProxy Makefile
22
# Easy build and deployment for SOCKS5 proxy network
33

4-
.PHONY: help build build-main build-dashboard build-dashboard-ui build-windows-agent build-darwin-agent build-linux-amd64 build-linux-arm64 install-agent-macos clean install deps test run-controller start-controller run-agent run-agent-dev docker-agent test-agent-docker docker-agent-down setup-dev check-deps format lint setup-system vps-setup setup-api-controller quickstart debug cleanup install-service install-dashboard-service install-production start-service stop-service start start-dev stop stop-production uninstall-production uninstall dashboard dashboard-dev dashboard-init dashboard-run dashboard-up run-dashboard sync-agent-key sync-deployment-settings setup-domain
4+
.PHONY: help build build-main build-dashboard build-dashboard-ui build-windows-agent build-darwin-agent build-linux-amd64 build-linux-arm64 install-agent-macos clean install deps test run-controller start-controller run-agent run-agent-dev docker-agent test-agent-docker docker-agent-down setup-dev check-deps format lint setup-system vps-setup setup-api-controller quickstart debug cleanup install-service install-dashboard-service install-production start-service stop-service start start-dev stop stop-production uninstall-production uninstall dashboard dashboard-dev dashboard-init dashboard-run dashboard-up run-dashboard sync-agent-key sync-deployment-settings setup-domain reset-dashboard-admin reset-dashboard-admin-dev fix-dev-permissions
55

66
# Catch accidental "make run dashboard" (space) — the target is run-dashboard (hyphen).
77
ifneq (,$(filter dashboard,$(MAKECMDGOALS)))
@@ -25,12 +25,16 @@ help:
2525
@echo "TrinityProxy"
2626
@echo "============"
2727
@echo ""
28-
@echo " make start - PRODUCTION: install + run on VPS (deps, build, secrets, systemd)"
28+
@echo " make start - PRODUCTION (Linux VPS): install + run via systemd"
29+
@echo " On macOS: same as make start-dev (local only)"
2930
@echo " make uninstall-production - Remove prod install (systemd, /opt, data, config); fresh: sudo make start"
3031
@echo " make uninstall - Alias for uninstall-production"
3132
@echo " make setup-domain - VPS: interactive domain + Cloudflare wildcard SSL (sudo)"
3233
@echo " make start-dev - LOCAL DEV: Vite :8080, dashboard API :8081, controller :3100"
34+
@echo " Uses .dev/*.db — never touches production VPS"
3335
@echo " make stop - Stop local dev servers"
36+
@echo " make reset-dashboard-admin-dev - Reset admin login in .dev/dashboard.db (no sudo)"
37+
@echo " make fix-dev-permissions - Fix root-owned web/dashboard/dist after sudo make start"
3438
@echo " make run-agent-dev - macOS/local dev agent (embedded SOCKS :1080, foreground)"
3539
@echo " make build - Build all binaries"
3640

@@ -235,6 +239,21 @@ run: build
235239
@echo "[*] Starting TrinityProxy with interactive setup..."
236240
@export PATH="/usr/local/go/bin:$$PATH"; ./$(BUILD_DIR)/$(BINARY_NAME)
237241

242+
# Load dev or controller env for agent/controller targets
243+
define load-dev-env
244+
set -a; \
245+
if [ -f scripts/lib/dev-env.sh ]; then \
246+
. scripts/lib/dev-env.sh; \
247+
dev_env_apply; \
248+
if [ -f "$$DEV_CONTROLLER_ENV" ]; then \
249+
. "$$DEV_CONTROLLER_ENV"; \
250+
fi; \
251+
elif [ -f .env.controller ]; then \
252+
. ./.env.controller; \
253+
fi; \
254+
set +a;
255+
endef
256+
238257
# Load .env.controller (TRINITY_AGENT_KEY) when present — written by make sync-agent-key
239258
define load-controller-env
240259
set -a; \
@@ -307,19 +326,11 @@ run-agent-dev: build-main
307326
@echo ""
308327
@echo "============================================"
309328
@echo " macOS dev mode — embedded SOCKS on :1080 (no Dante)"
329+
@echo " LOCAL ONLY — heartbeats go to localhost"
310330
@echo "============================================"
311331
@echo ""
312-
@if [ -f .env.controller ]; then \
313-
echo "[*] Loading .env.controller (TRINITY_AGENT_KEY)"; \
314-
else \
315-
echo "[!] No .env.controller — run 'make sync-agent-key' after dashboard generates an agent key"; \
316-
fi
317-
@echo "[*] Controller: $${CONTROLLER_URL:-http://127.0.0.1:3100}"
318-
@echo "[*] SOCKS5: :$${TRINITY_SOCKS_PORT:-1080} (user=dev, pass=dev)"
319-
@echo "[*] Press Ctrl+C to stop."
320-
@echo ""
321332
@export PATH="/usr/local/go/bin:$$PATH"; \
322-
$(load-controller-env) \
333+
$(load-dev-env) \
323334
CONTROLLER_URL="$${CONTROLLER_URL:-http://127.0.0.1:3100}" \
324335
TRINITY_ROLE=agent \
325336
TRINITY_DEV=1 \
@@ -386,7 +397,12 @@ dev-agent: build-main
386397
fi
387398

388399
# Production — full VPS bootstrap (deps, build, secrets, systemd, credentials)
400+
# On macOS, delegates to start-dev (local only).
389401
start:
402+
@if [ "$$(uname -s)" = "Darwin" ]; then \
403+
echo "[*] macOS: make start runs LOCAL DEV (same as make start-dev)."; \
404+
echo "[*] Do not use sudo. Production runs on your Linux VPS."; \
405+
fi
390406
@chmod +x scripts/start-production.sh
391407
@./scripts/start-production.sh
392408

@@ -441,6 +457,17 @@ reset-dashboard-admin reset-admin:
441457
@chmod +x scripts/reset-dashboard-admin.sh scripts/lib/production-common.sh
442458
@./scripts/reset-dashboard-admin.sh
443459

460+
461+
# Fix root-owned dashboard UI dirs after accidental sudo make start (macOS local dev)
462+
fix-dev-permissions:
463+
@chmod +x scripts/fix-dev-permissions.sh scripts/lib/dev-ui-permissions.sh
464+
@./scripts/fix-dev-permissions.sh
465+
466+
# Reset admin in local dev database (.dev/dashboard.db) — no sudo
467+
reset-dashboard-admin-dev:
468+
@chmod +x scripts/reset-dashboard-admin.sh scripts/lib/production-common.sh
469+
@TRINITY_DEV=1 DASHBOARD_DB_PATH="$(CURDIR)/.dev/dashboard.db" DASHBOARD_URL="http://localhost:8080" ./scripts/reset-dashboard-admin.sh
470+
444471
# Bootstrap initial dashboard admin (prints temp credentials once)
445472
dashboard-init: build-dashboard
446473
@echo "[*] Initializing dashboard admin user..."

‎cmd/dashboard/main.go‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ func main() {
101101
}
102102
defer deployStore.Close()
103103

104-
if result, err := deployStore.SyncFromExternal(deploymentSyncOptions(false)); err != nil {
104+
if isDevMode() {
105+
log.Info("dev mode: skipping external deployment sync (local-only)",
106+
"controller_url", cfg.ControllerURL,
107+
)
108+
} else if result, err := deployStore.SyncFromExternal(deploymentSyncOptions(false)); err != nil {
105109
log.Warn("deployment settings sync skipped", "err", err)
106110
} else if result.Updated {
107111
log.Info("deployment settings synced from host config",
@@ -257,6 +261,14 @@ func registerStaticUI(mux *http.ServeMux, staticDir string, log *slog.Logger) {
257261

258262

259263

264+
func isDevMode() bool {
265+
switch strings.ToLower(strings.TrimSpace(os.Getenv("TRINITY_ENV"))) {
266+
case "development", "dev":
267+
return true
268+
}
269+
return strings.TrimSpace(os.Getenv("TRINITY_DEV")) == "1"
270+
}
271+
260272
func deploymentSyncOptions(force bool) deployment.SyncOptions {
261273
return deployment.SyncOptions{
262274
Sources: deployment.ExternalSources{

‎scripts/build-dashboard-ui.sh‎

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,136 @@ set -euo pipefail
88
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
99
cd "$ROOT"
1010

11+
# shellcheck source=scripts/lib/dev-ui-permissions.sh
12+
source "$ROOT/scripts/lib/dev-ui-permissions.sh"
13+
1114
SRC_DIST="$ROOT/web/dashboard/dist"
1215
EMBED_DIST="$ROOT/cmd/dashboard/dist"
16+
FALLBACK_DIST="$ROOT/.dev/vite-dist"
17+
# Vite runs with cwd web/dashboard — use a path relative to that directory.
18+
FALLBACK_VITE_OUT="../../.dev/vite-dist"
19+
20+
DID_NPM_BUILD=0
21+
BUILT_DIST=""
1322

1423
embed_tree_valid() {
1524
[[ -f "$EMBED_DIST/index.html" ]]
1625
}
1726

1827
src_tree_valid() {
19-
[[ -f "$SRC_DIST/index.html" ]]
28+
local dir="${1:-$SRC_DIST}"
29+
[[ -f "$dir/index.html" ]]
30+
}
31+
32+
resolve_trinity_vite_out_dir() {
33+
local raw="${TRINITY_VITE_OUT_DIR:-}"
34+
local abs
35+
if [[ -z "$raw" ]]; then
36+
return 1
37+
fi
38+
if [[ "$raw" = /* ]]; then
39+
abs="$raw"
40+
else
41+
abs="$ROOT/$raw"
42+
fi
43+
printf '%s\n' "$abs"
44+
}
45+
46+
vite_out_rel_from_repo_path() {
47+
local repo_rel="${1#./}"
48+
printf '../../%s\n' "$repo_rel"
2049
}
2150

2251
embed_matches_src() {
23-
src_tree_valid && embed_tree_valid && cmp -s "$SRC_DIST/index.html" "$EMBED_DIST/index.html"
52+
local dir="${1:-$SRC_DIST}"
53+
src_tree_valid "$dir" && embed_tree_valid && cmp -s "$dir/index.html" "$EMBED_DIST/index.html"
2454
}
2555

2656
sync_dist_to_embed() {
27-
if embed_matches_src; then
57+
local src_dir="${1:-$SRC_DIST}"
58+
local force="${2:-0}"
59+
60+
if [[ "$force" != "1" ]] && embed_matches_src "$src_dir"; then
2861
echo "[+] Embed UI already up to date at $EMBED_DIST"
2962
touch "$EMBED_DIST/.ui-sync-stamp"
3063
return 0
3164
fi
3265

33-
if ! src_tree_valid; then
34-
echo "[-] Missing $SRC_DIST/index.html"
66+
if ! src_tree_valid "$src_dir"; then
67+
echo "[-] Missing $src_dir/index.html"
3568
exit 1
3669
fi
3770

71+
if ! dev_ui_dir_writable "$EMBED_DIST"; then
72+
dev_ui_fix_dist_permissions "$ROOT" 1 || {
73+
echo "[-] Cannot sync UI — $EMBED_DIST is not writable."
74+
echo " Run: make fix-dev-permissions"
75+
exit 1
76+
}
77+
fi
78+
3879
mkdir -p "$EMBED_DIST"
3980
if command -v rsync >/dev/null 2>&1; then
40-
rsync -a --delete "$SRC_DIST/" "$EMBED_DIST/"
81+
rsync -a --delete "$src_dir/" "$EMBED_DIST/"
4182
else
4283
rm -rf "${EMBED_DIST:?}"/*
43-
cp -a "$SRC_DIST"/. "$EMBED_DIST/"
84+
cp -a "$src_dir"/. "$EMBED_DIST/"
4485
fi
4586
echo "[+] Synced UI assets to $EMBED_DIST (go:embed in cmd/dashboard)"
4687
touch "$EMBED_DIST/.ui-sync-stamp"
4788
}
4889

4990
build_with_npm() {
91+
DID_NPM_BUILD=1
92+
5093
if [[ ! -d "$ROOT/web/dashboard/node_modules" ]]; then
5194
echo "[*] Installing dashboard UI dependencies..."
5295
(cd "$ROOT/web/dashboard" && npm install)
5396
fi
97+
98+
local vite_out_rel="dist"
99+
BUILT_DIST="$SRC_DIST"
100+
101+
if abs="$(resolve_trinity_vite_out_dir 2>/dev/null || true)" && [[ -n "$abs" ]]; then
102+
echo "[*] Using TRINITY_VITE_OUT_DIR=$abs"
103+
BUILT_DIST="$abs"
104+
if [[ "${TRINITY_VITE_OUT_DIR:-}" = /* ]]; then
105+
vite_out_rel="$abs"
106+
else
107+
vite_out_rel="$(vite_out_rel_from_repo_path "${TRINITY_VITE_OUT_DIR}")"
108+
fi
109+
rm -rf "$abs"
110+
elif [[ -d "$SRC_DIST" ]] && ! rm -rf "$SRC_DIST" 2>/dev/null; then
111+
echo "[!] $SRC_DIST is not writable (often root-owned after 'sudo make start')."
112+
echo " Building to $FALLBACK_DIST instead."
113+
echo " To fix permanently: make fix-dev-permissions (or: sudo rm -rf $SRC_DIST)"
114+
BUILT_DIST="$FALLBACK_DIST"
115+
vite_out_rel="$FALLBACK_VITE_OUT"
116+
rm -rf "$FALLBACK_DIST"
117+
fi
118+
54119
echo "[*] Building dashboard UI (npm run build)..."
55-
(cd "$ROOT/web/dashboard" && npm run build)
120+
if [[ "$BUILT_DIST" == "$SRC_DIST" ]] && [[ -z "${TRINITY_VITE_OUT_DIR:-}" ]]; then
121+
(cd "$ROOT/web/dashboard" && npm run build)
122+
else
123+
(cd "$ROOT/web/dashboard" && npm run build -- --outDir "$vite_out_rel" --emptyOutDir)
124+
fi
125+
echo ""
126+
127+
if ! src_tree_valid "$BUILT_DIST"; then
128+
echo "[-] Vite build finished but $BUILT_DIST/index.html is missing."
129+
exit 1
130+
fi
56131
}
57132

58133
if command -v npm >/dev/null 2>&1; then
59134
build_with_npm
60135
elif src_tree_valid; then
61136
echo "[+] npm not found — using existing $SRC_DIST"
137+
BUILT_DIST="$SRC_DIST"
138+
elif src_tree_valid "$FALLBACK_DIST"; then
139+
echo "[+] npm not found — using existing $FALLBACK_DIST"
140+
BUILT_DIST="$FALLBACK_DIST"
62141
elif embed_tree_valid; then
63142
echo "[+] npm not found — reusing embedded UI tree at $EMBED_DIST"
64143
exit 0
@@ -68,4 +147,9 @@ else
68147
exit 1
69148
fi
70149

71-
sync_dist_to_embed
150+
if [[ -z "$BUILT_DIST" ]]; then
151+
echo "[-] Internal error: UI build output directory not set."
152+
exit 1
153+
fi
154+
155+
sync_dist_to_embed "$BUILT_DIST" "$DID_NPM_BUILD"

‎scripts/fix-dev-permissions.sh‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Restore user ownership on dashboard UI dist dirs (after accidental sudo make start on macOS).
4+
5+
set -euo pipefail
6+
7+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
8+
# shellcheck source=scripts/lib/dev-ui-permissions.sh
9+
source "$ROOT/scripts/lib/dev-ui-permissions.sh"
10+
11+
auto=0
12+
for arg in "$@"; do
13+
case "$arg" in
14+
--auto) auto=1 ;;
15+
esac
16+
done
17+
18+
dev_ui_fix_dist_permissions "$ROOT" "$auto"

0 commit comments

Comments
 (0)