Skip to content

Commit 3aa7d55

Browse files
committed
feat: add proxy metadata storage and Bootstrap Icons update script
- Add proxy metadata storage system for enabled/disabled state management - Implement proxy migration from Caddy to metadata storage - Add autostart functionality for proxies with enabled flag - Create Bootstrap Icons update script with version support - Fix Makefile shell command syntax for build metadata - Update Caddy startup order to ensure API availability for Web UI - Add comprehensive Bootstrap Icons sprite with new icons - Update AGENTS.md with Bootstrap Icons management documentation
1 parent 870fe3b commit 3aa7d55

File tree

11 files changed

+778
-137
lines changed

11 files changed

+778
-137
lines changed

AGENTS.md

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,24 @@ After migration, remove `RELAY_LIST` and manage relays through Web UI.
229229
The SPA uses a lightweight Bootstrap Icons SVG sprite at:
230230
- `webui/cmd/webui/web/static/vendor/bootstrap-icons/bootstrap-icons.svg`
231231

232-
If swapping in full Bootstrap Icons distribution, keep sprite in same path or update template references.
232+
To update Bootstrap Icons to the latest version:
233+
234+
```bash
235+
# Update to latest version
236+
./update-bootstrap-icons.sh
237+
238+
# Update to specific version
239+
./update-bootstrap-icons.sh 1.11.3
240+
```
241+
242+
The script will:
243+
1. Download the specified (or latest) Bootstrap Icons release
244+
2. Backup the current sprite file
245+
3. Extract and install the new sprite
246+
4. Show file size and icon count comparison
247+
5. Provide next steps for testing and committing
248+
249+
If swapping in full Bootstrap Icons distribution manually, keep sprite in same path or update template references.
233250

234251
## Code Style Guidelines
235252

@@ -290,19 +307,20 @@ If swapping in full Bootstrap Icons distribution, keep sprite in same path or up
290307

291308
```
292309
.
293-
├── Dockerfile # Multi-stage container build (includes building the Web UI binary in a builder stage)
294-
├── Dockerfile.dev # Development image that copies the locally-built `data/tailrelay-webui` binary
295-
├── start.sh # Container entrypoint: starts tailscaled, Web UI, optional socat relays, and Caddy
296-
├── webui/ # Go Web UI source tree (see details below)
297-
├── webui.yaml # Default runtime config for the Web UI included in the image
298-
├── data/ # Local build outputs (e.g., `tailrelay-webui` produced by `make dev-build`)
299-
├── compose-test.yml # Docker Compose config used for development and integration testing
300-
├── docker-compose-test.py # Python-driven integration test harness (env-driven, curl checks)
301-
├── docker-compose-test.sh # Bash wrapper test script for quick runs
302-
├── test_proxy_api.sh # Example script that exercises Web UI/Caddy API endpoints (uses `curl`)
303-
├── requirements.txt # Python dependencies for the test harness (python-dotenv, etc.)
304-
├── Caddyfile.example # Example/legacy Caddyfile for manual Caddy configuration or troubleshooting
305-
└── README.md # Project overview and developer documentation
310+
├── Dockerfile # Multi-stage container build (includes building the Web UI binary in a builder stage)
311+
├── Dockerfile.dev # Development image that copies the locally-built `data/tailrelay-webui` binary
312+
├── start.sh # Container entrypoint: starts tailscaled, Web UI, optional socat relays, and Caddy
313+
├── webui/ # Go Web UI source tree (see details below)
314+
├── webui.yaml # Default runtime config for the Web UI included in the image
315+
├── data/ # Local build outputs (e.g., `tailrelay-webui` produced by `make dev-build`)
316+
├── compose-test.yml # Docker Compose config used for development and integration testing
317+
├── docker-compose-test.py # Python-driven integration test harness (env-driven, curl checks)
318+
├── docker-compose-test.sh # Bash wrapper test script for quick runs
319+
├── test_proxy_api.sh # Example script that exercises Web UI/Caddy API endpoints (uses `curl`)
320+
├── update-bootstrap-icons.sh # Script to update Bootstrap Icons SVG sprite to latest or specific version
321+
├── requirements.txt # Python dependencies for the test harness (python-dotenv, etc.)
322+
├── Caddyfile.example # Example/legacy Caddyfile for manual Caddy configuration or troubleshooting
323+
└── README.md # Project overview and developer documentation
306324
```
307325

308326
## Testing Strategy

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
.PHONY: frontend-build dev-build dev-docker-build clean help
22

33
# Build metadata
4-
VERSION ?= $(git describe --tags --always --dirty 2>/dev/null || echo "dev")
5-
COMMIT ?= $(git rev-parse --short HEAD 2>/dev/null || echo "none")
6-
DATE ?= $(date -u +%Y-%m-%dT%H:%M:%SZ)
7-
BRANCH ?= $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
8-
BUILDER ?= $(whoami)
4+
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
5+
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
6+
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
7+
BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
8+
BUILDER ?= $(shell whoami)
99

1010
# Go build flags with metadata
1111
LDFLAGS = -w -s \

start.sh

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ else
4343
fi
4444

4545

46+
# Start Caddy first (before Web UI so migration/discovery can connect)
47+
echo -n "Starting Caddy... "
48+
CADDY_STATUS=$(caddy start --config /etc/caddy/Caddyfile >/dev/null)
49+
# echo success or fail + stderr
50+
if [ $? -ne 0 ]; then
51+
echo "failed!"
52+
echo $CADDY_STATUS
53+
else
54+
echo "success!"
55+
fi
56+
57+
# Wait briefly for Caddy API to be ready
58+
sleep 1
59+
4660
# Start Web UI
4761
echo -n "Starting Tailrelay Web UI... "
4862
/usr/bin/tailrelay-webui --config /etc/tailrelay/webui.yaml > /var/log/tailrelay-webui.log 2>&1 &
@@ -82,15 +96,4 @@ if [ ! -z "$RELAY_LIST" ]; then
8296
done
8397
fi
8498

85-
# Start Caddy
86-
echo -n "Starting Caddy... "
87-
CADDY_STATUS=$(caddy start --config /etc/caddy/Caddyfile >/dev/null)
88-
# echo success or fail + stderr
89-
if [ $? -ne 0 ]; then
90-
echo "failed!"
91-
echo $CADDY_STATUS
92-
else
93-
echo "success!"
94-
fi
95-
9699
wait $TAILSCALED_PID $WEBUI_PID

update-bootstrap-icons.sh

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/env bash
2+
# Update Bootstrap Icons SVG sprite for the Web UI
3+
# Usage: ./update-bootstrap-icons.sh [version]
4+
#
5+
# If no version is specified, the script fetches the latest release.
6+
# Examples:
7+
# ./update-bootstrap-icons.sh # Get latest version
8+
# ./update-bootstrap-icons.sh 1.11.3 # Get specific version
9+
10+
set -e
11+
12+
# Colors for output
13+
RED='\033[0;31m'
14+
GREEN='\033[0;32m'
15+
YELLOW='\033[1;33m'
16+
BLUE='\033[0;34m'
17+
NC='\033[0m' # No Color
18+
19+
# Target path for the sprite
20+
TARGET_PATH="webui/cmd/webui/web/static/vendor/bootstrap-icons/bootstrap-icons.svg"
21+
TEMP_DIR=$(mktemp -d)
22+
23+
# Cleanup on exit
24+
cleanup() {
25+
rm -rf "$TEMP_DIR"
26+
}
27+
trap cleanup EXIT
28+
29+
# Print colored message
30+
log_info() {
31+
echo -e "${BLUE}${NC} $1"
32+
}
33+
34+
log_success() {
35+
echo -e "${GREEN}${NC} $1"
36+
}
37+
38+
log_warning() {
39+
echo -e "${YELLOW}${NC} $1"
40+
}
41+
42+
log_error() {
43+
echo -e "${RED}${NC} $1"
44+
}
45+
46+
# Get latest version from GitHub API
47+
get_latest_version() {
48+
local version=$(curl -sSL https://api.github.com/repos/twbs/icons/releases/latest | grep '"tag_name":' | sed -E 's/.*"v?([^"]+)".*/\1/')
49+
if [ -z "$version" ]; then
50+
log_error "Failed to fetch latest version"
51+
exit 1
52+
fi
53+
echo "$version"
54+
}
55+
56+
# Validate version format
57+
validate_version() {
58+
local version=$1
59+
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
60+
log_error "Invalid version format: $version (expected: X.Y.Z)"
61+
exit 1
62+
fi
63+
}
64+
65+
# Download and extract Bootstrap Icons
66+
download_icons() {
67+
local version=$1
68+
local download_url="https://github.com/twbs/icons/releases/download/v${version}/bootstrap-icons-${version}.zip"
69+
70+
log_info "Downloading Bootstrap Icons v${version}..."
71+
72+
if ! curl -sSL "$download_url" -o "$TEMP_DIR/bootstrap-icons.zip"; then
73+
log_error "Failed to download Bootstrap Icons v${version}"
74+
log_error "URL: $download_url"
75+
exit 1
76+
fi
77+
78+
log_success "Downloaded Bootstrap Icons v${version}"
79+
80+
log_info "Extracting archive..."
81+
if ! unzip -q "$TEMP_DIR/bootstrap-icons.zip" -d "$TEMP_DIR"; then
82+
log_error "Failed to extract archive"
83+
exit 1
84+
fi
85+
86+
log_success "Extracted archive"
87+
}
88+
89+
# Find the sprite file in the extracted archive
90+
find_sprite() {
91+
local sprite_file=$(find "$TEMP_DIR" -name "bootstrap-icons.svg" -type f | grep -v node_modules | head -1)
92+
93+
if [ -z "$sprite_file" ]; then
94+
log_error "Could not find bootstrap-icons.svg in the downloaded archive"
95+
log_info "Contents of $TEMP_DIR:"
96+
find "$TEMP_DIR" -type f | head -20
97+
exit 1
98+
fi
99+
100+
echo "$sprite_file"
101+
}
102+
103+
# Backup current sprite
104+
backup_current() {
105+
if [ -f "$TARGET_PATH" ]; then
106+
local backup_path="${TARGET_PATH}.backup.$(date +%Y%m%d_%H%M%S)"
107+
log_info "Backing up current sprite to: $(basename $backup_path)"
108+
cp "$TARGET_PATH" "$backup_path"
109+
log_success "Backup created"
110+
else
111+
log_warning "No existing sprite found at $TARGET_PATH"
112+
fi
113+
}
114+
115+
# Compare file sizes
116+
compare_files() {
117+
local old_file=$1
118+
local new_file=$2
119+
120+
if [ ! -f "$old_file" ]; then
121+
log_info "No previous file to compare"
122+
return
123+
fi
124+
125+
local old_size=$(stat -f%z "$old_file" 2>/dev/null || stat -c%s "$old_file" 2>/dev/null)
126+
local new_size=$(stat -f%z "$new_file" 2>/dev/null || stat -c%s "$new_file" 2>/dev/null)
127+
local old_icons=$(grep -o '<symbol' "$old_file" | wc -l | tr -d ' ')
128+
local new_icons=$(grep -o '<symbol' "$new_file" | wc -l | tr -d ' ')
129+
130+
log_info "File comparison:"
131+
echo " Old: $(numfmt --to=iec-i --suffix=B $old_size 2>/dev/null || echo "${old_size} bytes") ($old_icons icons)"
132+
echo " New: $(numfmt --to=iec-i --suffix=B $new_size 2>/dev/null || echo "${new_size} bytes") ($new_icons icons)"
133+
134+
if [ "$new_icons" -gt "$old_icons" ]; then
135+
log_success "Added $(($new_icons - $old_icons)) new icons"
136+
elif [ "$new_icons" -lt "$old_icons" ]; then
137+
log_warning "Removed $(($old_icons - $new_icons)) icons"
138+
else
139+
log_info "Icon count unchanged"
140+
fi
141+
}
142+
143+
# Show help
144+
show_help() {
145+
cat << EOF
146+
Bootstrap Icons Updater for Tailrelay Web UI
147+
148+
Usage:
149+
./update-bootstrap-icons.sh [version]
150+
151+
Arguments:
152+
version Optional version number (e.g., 1.11.3)
153+
If not specified, the latest release will be fetched
154+
155+
Examples:
156+
./update-bootstrap-icons.sh # Get latest version
157+
./update-bootstrap-icons.sh 1.11.3 # Get specific version
158+
159+
This script will:
160+
1. Download Bootstrap Icons from GitHub releases
161+
2. Backup the current sprite file
162+
3. Extract and install the new sprite
163+
4. Compare file sizes and icon counts
164+
5. Provide next steps for testing and committing
165+
166+
EOF
167+
exit 0
168+
}
169+
170+
# Main function
171+
main() {
172+
local version=${1:-}
173+
174+
# Show help if requested
175+
if [ "$version" = "-h" ] || [ "$version" = "--help" ]; then
176+
show_help
177+
fi
178+
179+
# Header
180+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
181+
echo -e "${BLUE} Bootstrap Icons Updater for Tailrelay Web UI${NC}"
182+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
183+
echo
184+
185+
# Check dependencies
186+
for cmd in curl unzip grep sed find; do
187+
if ! command -v $cmd &> /dev/null; then
188+
log_error "Required command not found: $cmd"
189+
exit 1
190+
fi
191+
done
192+
193+
# Determine version
194+
if [ -z "$version" ]; then
195+
log_info "Fetching latest Bootstrap Icons version..."
196+
version=$(get_latest_version)
197+
log_success "Latest version: v${version}"
198+
else
199+
# Remove 'v' prefix if present
200+
version=${version#v}
201+
validate_version "$version"
202+
log_info "Using specified version: v${version}"
203+
fi
204+
205+
# Download and extract
206+
download_icons "$version"
207+
208+
# Find sprite file
209+
sprite_file=$(find_sprite)
210+
log_success "Found sprite: $(basename $(dirname $sprite_file))/$(basename $sprite_file)"
211+
212+
# Backup current file
213+
backup_current
214+
215+
# Compare files
216+
compare_files "$TARGET_PATH" "$sprite_file"
217+
218+
# Create target directory if it doesn't exist
219+
mkdir -p "$(dirname $TARGET_PATH)"
220+
221+
# Copy new sprite
222+
log_info "Installing new sprite..."
223+
cp "$sprite_file" "$TARGET_PATH"
224+
log_success "Sprite installed at: $TARGET_PATH"
225+
226+
# Verify installation
227+
if [ -f "$TARGET_PATH" ]; then
228+
local icon_count=$(grep -o '<symbol' "$TARGET_PATH" | wc -l | tr -d ' ')
229+
log_success "Installation verified: $icon_count icons available"
230+
else
231+
log_error "Installation verification failed"
232+
exit 1
233+
fi
234+
235+
# Next steps
236+
echo
237+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
238+
log_success "Bootstrap Icons updated successfully!"
239+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
240+
echo
241+
log_info "Next steps:"
242+
echo " 1. Review changes: git diff $TARGET_PATH"
243+
echo " 2. Test the Web UI: make dev-build && make dev-docker-build"
244+
echo " 3. Commit changes: git add $TARGET_PATH && git commit -m 'Update Bootstrap Icons to v${version}'"
245+
echo
246+
log_info "View available icons at:"
247+
echo " https://icons.getbootstrap.com/"
248+
echo
249+
}
250+
251+
# Run main function
252+
main "$@"

0 commit comments

Comments
 (0)