-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.sh
More file actions
210 lines (186 loc) · 6.42 KB
/
Copy pathsetup.sh
File metadata and controls
210 lines (186 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env bash
# Auto-detect LAN IP, write config.env, pull images, start stack.
set -euo pipefail
DEV=0
[[ "${1:-}" == "--dev" ]] && DEV=1
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"
CONFIG="$ROOT/config.env"
EXAMPLE="$ROOT/config.env.example"
is_lan_ip() {
local ip="$1"
[[ -z "$ip" ]] && return 1
[[ "$ip" =~ ^127\. ]] && return 1
[[ "$ip" =~ ^169\.254\. ]] && return 1
[[ "$ip" =~ ^192\.168\.65\. ]] && return 1
[[ "$ip" =~ ^172\.(17|18)\. ]] && return 1
[[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
return 0
}
rank_ip() {
case "$1" in
192.168.[0-9]*.[0-9]*) echo 100 ;;
10.*) echo 80 ;;
172.1[6-9].*|172.2[0-9].*|172.3[0-1].*) echo 60 ;;
*) echo 10 ;;
esac
}
is_docker_iface() {
case "$1" in
docker*|br-*|veth*|virbr*|podman*) return 0 ;;
esac
return 1
}
detect_lan_ip() {
local ip ifname candidates=() r best_ip="" best_rank=0
if command -v ip >/dev/null 2>&1; then
ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") print $(i+1)}')"
is_lan_ip "$ip" && candidates+=("$ip")
while read -r ifname ip; do
is_docker_iface "$ifname" && continue
is_lan_ip "$ip" && candidates+=("$ip")
done < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $2, $4}' | sed 's|/.*||')
fi
for ip in $(printf '%s\n' "${candidates[@]}" | sort -u); do
r=$(rank_ip "$ip")
if [ "$r" -gt "$best_rank" ]; then
best_rank=$r
best_ip=$ip
fi
done
echo "$best_ip"
}
new_session_secret() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 24
else
head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 48
fi
}
update_config_env() {
local lan_ip="$1"
local tmp has_lan=0 has_secret=0 has_https=0
[ -f "$EXAMPLE" ] || { echo "config.env.example missing" >&2; exit 1; }
[ -f "$CONFIG" ] || cp "$EXAMPLE" "$CONFIG"
tmp="$(mktemp)"
while IFS= read -r line || [ -n "$line" ]; do
if [[ "$line" =~ ^[[:space:]]*LAN_IP[[:space:]]*= ]]; then
echo "LAN_IP=$lan_ip"
has_lan=1
elif [[ "$line" =~ ^[[:space:]]*SESSION_SECRET[[:space:]]*= ]]; then
secret="${line#*=}"
secret="$(echo "$secret" | tr -d '[:space:]')"
if [ -z "$secret" ] || [ "$secret" = 'change-me-use-a-long-random-string' ]; then
echo "SESSION_SECRET=$(new_session_secret)"
else
echo "$line"
fi
has_secret=1
else
[[ "$line" =~ ^[[:space:]]*USE_HTTPS[[:space:]]*= ]] && has_https=1
echo "$line"
fi
done < "$CONFIG" > "$tmp"
[ "$has_lan" -eq 1 ] || echo "LAN_IP=$lan_ip" >> "$tmp"
[ "$has_secret" -eq 1 ] || echo "SESSION_SECRET=$(new_session_secret)" >> "$tmp"
[ "$has_https" -eq 1 ] || echo "USE_HTTPS=false" >> "$tmp"
mv "$tmp" "$CONFIG"
}
sync_compose_env() {
cp "$CONFIG" "$ROOT/.env"
}
check_docker_access() {
if docker info >/dev/null 2>&1; then
return 0
fi
echo "Docker permission error (cannot talk to the Docker daemon)." >&2
echo "" >&2
echo "Fix (pick one):" >&2
echo " 1) Add your user to the docker group, then log out and back in:" >&2
echo " sudo usermod -aG docker \"\$USER\"" >&2
echo " 2) Run this setup with sudo (quick test only):" >&2
echo " sudo ./setup.sh" >&2
echo "" >&2
echo "Also run compose from the folder that contains docker-compose.yml and config.env:" >&2
echo " cp config.env .env && docker compose up -d" >&2
return 1
}
compose() {
check_docker_access || exit 1
docker compose "$@"
}
LAN_IP="$(detect_lan_ip)"
[ -n "$LAN_IP" ] || { echo "Could not detect LAN IP" >&2; exit 1; }
echo "Using LAN_IP=$LAN_IP"
update_config_env "$LAN_IP"
sync_compose_env
if [ "$DEV" -eq 1 ]; then
echo "Dev mode: building locally..."
compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
else
echo "Pulling images..."
if ! compose pull; then
echo "" >&2
echo "Image pull failed (often 'permission denied' from GHCR)." >&2
echo "" >&2
echo "Check config.env image lines, for example:" >&2
grep -E '^[[:space:]]*TESLACAM_.*_IMAGE[[:space:]]*=' "$CONFIG" 2>/dev/null | head -n 5 >&2 || true
echo "" >&2
echo "Fix A (recommended): make packages public on GitHub:" >&2
echo " github.com/squishylemon?tab=packages -> each teslacam-* package -> Package settings -> Public" >&2
echo "" >&2
echo "Fix B: log in to GHCR (private packages):" >&2
echo " echo YOUR_GITHUB_TOKEN | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin" >&2
echo " (token needs read:packages scope)" >&2
echo "" >&2
echo "Fix C: build locally instead of pull:" >&2
echo " ./setup.sh --dev" >&2
exit 1
fi
echo "Starting stack..."
compose up -d
fi
PORT=4321
if grep -qE '^[[:space:]]*WEB_PORT[[:space:]]*=' "$CONFIG"; then
PORT=$(grep -E '^[[:space:]]*WEB_PORT[[:space:]]*=' "$CONFIG" | head -n1 | cut -d= -f2 | tr -d '[:space:]')
fi
PROTO=http
grep -qE '^[[:space:]]*USE_HTTPS[[:space:]]*=[[:space:]]*true' "$CONFIG" && PROTO=https
configure_linux_mdns() {
[ "$(uname -s)" = "Linux" ] || return 0
local mode
mode="$(grep -E '^[[:space:]]*MDNS_MODE[[:space:]]*=' "$CONFIG" 2>/dev/null | head -n1 | cut -d= -f2- | tr -d '[:space:]')"
mode="${mode:-auto}"
if [ "$mode" = "off" ]; then
compose stop host-mdns >/dev/null 2>&1 || true
bash "$ROOT/scripts/linux-host-mdns.sh" stop >/dev/null 2>&1 || true
echo "[mdns] Disabled (MDNS_MODE=off)"
return 0
fi
if [ "$mode" = "host" ] || { [ "$mode" = "auto" ] && command -v avahi-publish >/dev/null 2>&1; }; then
compose stop host-mdns >/dev/null 2>&1 || true
if bash "$ROOT/scripts/linux-host-mdns.sh" start; then
echo "[mdns] Using host Avahi (recommended on Linux)"
return 0
fi
echo "[mdns] Host Avahi failed, falling back to container host-mdns"
compose up -d host-mdns
return 0
fi
echo "[mdns] Using container host-mdns"
echo "[mdns] If teslacam.local does not resolve, install avahi-utils and rerun setup"
}
configure_linux_mdns
HOSTNAME=teslacam.local
if grep -qE '^[[:space:]]*SITE_HOSTNAME[[:space:]]*=' "$CONFIG"; then
HOSTNAME=$(grep -E '^[[:space:]]*SITE_HOSTNAME[[:space:]]*=' "$CONFIG" | head -n1 | cut -d= -f2- | tr -d '[:space:]')
fi
echo ""
echo "=== Open the site ==="
echo " ${PROTO}://${HOSTNAME}:${PORT}"
echo " ${PROTO}://${LAN_IP}:${PORT}"
echo ""
echo "If ${HOSTNAME} does not resolve, add this line to /etc/hosts on the client device:"
echo " ${LAN_IP} ${HOSTNAME}"
echo ""
echo "Developers: ./setup.sh --dev"