最后活跃于 1 month ago

🚀 Автоматизирано изграждане на защитен хостинг с Cloudflare Tunnel & Docker и Wordpress сайтове

setup_hosting.sh 原始文件
1#!/bin/bash
2# =============================================================================
3# Modular Hosting Setup with optional Cloudflare Tunnel
4# Author Fedya Serafiev
5# Site itpraktika.com
6# =============================================================================
7
8set -euo pipefail
9
10# -- Colors -------------------------------------------------------------------
11RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
12CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
13
14info() { echo -e "${CYAN}[INFO] $*${RESET}"; }
15success() { echo -e "${GREEN}[OK] $*${RESET}"; }
16warn() { echo -e "${YELLOW}[WARN] $*${RESET}"; }
17error() { echo -e "${RED}[ERROR] $*${RESET}" >&2; exit 1; }
18
19gen_pass() {
20 openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 24
21}
22validate_yn() {
23 [[ "$1" =~ ^[yYnN]$ ]] || error "Vuvedete 'y' ili 'n'."
24}
25validate_count() {
26 [[ "$1" =~ ^[0-2]$ ]] || error "Vuvedete 0, 1 ili 2."
27}
28
29# -- Create DB user directly in running MariaDB -------------------------------
30create_db_user() {
31 local db_name="$1"
32 local db_user="$2"
33 local db_pass="$3"
34 local root_pass="$4"
35 local db_container="$5"
36
37 info "Creating database '$db_name' and user '$db_user' ..."
38 docker exec "$db_container" mariadb -u root -p"${root_pass}" -e "
39 CREATE DATABASE IF NOT EXISTS ${db_name} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
40 CREATE USER IF NOT EXISTS '${db_user}'@'%' IDENTIFIED BY '${db_pass}';
41 GRANT ALL PRIVILEGES ON ${db_name}.* TO '${db_user}'@'%';
42 FLUSH PRIVILEGES;
43 " && success "Database '$db_name' ready." || error "Failed to create database '$db_name'."
44}
45
46# -- Find running container by partial name -----------------------------------
47find_container() {
48 docker ps --format '{{.Names}}' 2>/dev/null | grep "$1" | head -1
49}
50
51# =============================================================================
52# Detect already running containers
53# =============================================================================
54CF_CONTAINER=$(find_container "cloudflared")
55KUMA_CONTAINER=$(find_container "uptime-kuma")
56WP1_CONTAINER=$(find_container "wp1")
57WP2_CONTAINER=$(find_container "wp2")
58DB_CONTAINER=$(find_container "db")
59
60HAVE_CF=false; [[ -n "$CF_CONTAINER" ]] && HAVE_CF=true
61HAVE_KUMA=false; [[ -n "$KUMA_CONTAINER" ]] && HAVE_KUMA=true
62HAVE_WP1=false; [[ -n "$WP1_CONTAINER" ]] && HAVE_WP1=true
63HAVE_WP2=false; [[ -n "$WP2_CONTAINER" ]] && HAVE_WP2=true
64HAVE_DB=false; [[ -n "$DB_CONTAINER" ]] && HAVE_DB=true
65
66SERVER_IP=$(hostname -I | awk '{print $1}')
67
68# =============================================================================
69echo -e "\n${BOLD}================================================${RESET}"
70echo -e "${BOLD} Modular Hosting Setup - Izberete moduli ${RESET}"
71echo -e "${BOLD}================================================${RESET}\n"
72
73echo -e "${BOLD}Tekushto sastoyanie:${RESET}"
74$HAVE_CF && echo -e " ${GREEN}[running]${RESET} Cloudflare Tunnel" || echo -e " ${YELLOW}[not installed]${RESET} Cloudflare Tunnel"
75$HAVE_KUMA && echo -e " ${GREEN}[running]${RESET} Uptime Kuma" || echo -e " ${YELLOW}[not installed]${RESET} Uptime Kuma"
76$HAVE_WP1 && echo -e " ${GREEN}[running]${RESET} WordPress 1" || echo -e " ${YELLOW}[not installed]${RESET} WordPress 1"
77$HAVE_WP2 && echo -e " ${GREEN}[running]${RESET} WordPress 2" || echo -e " ${YELLOW}[not installed]${RESET} WordPress 2"
78echo ""
79
80# =============================================================================
81# [1/4] What to ADD
82# =============================================================================
83echo -e "${BOLD}[1/4] Koe iskate da DOBAVITE:${RESET}\n"
84
85ADD_CF="n"; ADD_KUMA="n"; ADD_WP1=false; ADD_WP2=false
86
87if $HAVE_CF; then
88 info "Cloudflare Tunnel - veche e instaliran, preskachame."
89else
90 read -rp " Instaliraj Cloudflare Tunnel? (y/n): " ADD_CF
91 validate_yn "$ADD_CF"; ADD_CF="${ADD_CF,,}"
92fi
93
94if $HAVE_KUMA; then
95 info "Uptime Kuma - veche e instaliran, preskachame."
96else
97 read -rp " Instaliraj Uptime Kuma? (y/n): " ADD_KUMA
98 validate_yn "$ADD_KUMA"; ADD_KUMA="${ADD_KUMA,,}"
99fi
100
101if $HAVE_WP1 && $HAVE_WP2; then
102 info "I dvata WordPress sajta veche sa instalиrani, preskachame."
103elif $HAVE_WP1 && ! $HAVE_WP2; then
104 info "WordPress 1 veche e instaliran."
105 read -rp " Dobavi WordPress 2? (y/n): " _add2
106 validate_yn "$_add2"
107 [[ "${_add2,,}" == "y" ]] && ADD_WP2=true
108elif ! $HAVE_WP1 && ! $HAVE_WP2; then
109 read -rp " Broy WordPress sajtove? (0/1/2): " _wpc
110 validate_count "$_wpc"
111 [[ "$_wpc" -ge 1 ]] && ADD_WP1=true
112 [[ "$_wpc" -ge 2 ]] && ADD_WP2=true
113fi
114
115# Nothing new?
116if [[ "$ADD_CF" == "n" && "$ADD_KUMA" == "n" ]] && ! $ADD_WP1 && ! $ADD_WP2; then
117 warn "Nyama nishto novo za dobavyane. Izlizame."
118 exit 0
119fi
120
121# =============================================================================
122# [2/4] Cloudflare config
123# =============================================================================
124CLOUDFLARED_CMD=""
125CF_ENV_LINE=""
126CF_TOKEN_IN_ENV="# Cloudflare Tunnel not installed"
127cf_method=""
128
129if [[ "$ADD_CF" == "y" ]]; then
130 echo -e "\n${BOLD}[2/4] Cloudflare Tunnel - metod${RESET}"
131 echo " 1) Token - gotov token ot Cloudflare Dashboard"
132 echo " 2) Login - cloudflared se logva sam (URL)"
133 echo ""
134 read -rp " Izberi metod [1/2]: " cf_method
135 case "$cf_method" in
136 1)
137 read -rp " Tunnel Token: " cf_token
138 [[ -n "$cf_token" ]] || error "Tokenat ne mozhe da e prazen."
139 CLOUDFLARED_CMD='tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}'
140 CF_ENV_LINE=" - TUNNEL_TOKEN=\${TUNNEL_TOKEN}"
141 CF_TOKEN_IN_ENV="TUNNEL_TOKEN=$cf_token"
142 ;;
143 2)
144 CLOUDFLARED_CMD='tunnel --no-autoupdate login'
145 CF_TOKEN_IN_ENV="# TUNNEL_TOKEN= (login method)"
146 cf_token=""
147 ;;
148 *) error "Nevaliden izbor." ;;
149 esac
150fi
151
152# =============================================================================
153# [3/4] Passwords — keep existing, generate only what is missing
154# =============================================================================
155echo -e "\n${BOLD}[3/4] Paroli...${RESET}"
156
157[[ -f .env ]] && set -a && source .env 2>/dev/null && set +a || true
158
159DB_ROOT_PASS="${DB_ROOT_PASSWORD:-$(gen_pass)}"
160WP1_DB_PASS="${WP1_DB_PASSWORD:-$(gen_pass)}"
161WP2_DB_PASS="${WP2_DB_PASSWORD:-$(gen_pass)}"
162
163# =============================================================================
164# [4/4] Generate docker-compose.yml
165# =============================================================================
166echo -e "\n${BOLD}[4/4] Generiranje na fajlove...${RESET}"
167
168# -- .env ---------------------------------------------------------------------
169cat > .env <<ENVEOF
170# -- Cloudflare ---------------------------------------------------------------
171$CF_TOKEN_IN_ENV
172
173# -- Database -----------------------------------------------------------------
174DB_ROOT_PASSWORD=$DB_ROOT_PASS
175WP1_DB_PASSWORD=$WP1_DB_PASS
176WP2_DB_PASSWORD=$WP2_DB_PASS
177ENVEOF
178chmod 600 .env
179success ".env updated"
180
181# -- db-init (only for fresh installs, no DB running yet) ---------------------
182if ! $HAVE_DB; then
183 mkdir -p db-init
184 SQL=""
185 $ADD_WP1 && SQL+="CREATE DATABASE IF NOT EXISTS wp1_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nCREATE USER IF NOT EXISTS 'wp1_user'@'%' IDENTIFIED BY '${WP1_DB_PASS}';\nGRANT ALL PRIVILEGES ON wp1_db.* TO 'wp1_user'@'%';\n"
186 $ADD_WP2 && SQL+="CREATE DATABASE IF NOT EXISTS wp2_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nCREATE USER IF NOT EXISTS 'wp2_user'@'%' IDENTIFIED BY '${WP2_DB_PASS}';\nGRANT ALL PRIVILEGES ON wp2_db.* TO 'wp2_user'@'%';\n"
187 SQL+="FLUSH PRIVILEGES;\n"
188 printf "%b" "$SQL" > db-init/01-init.sql
189 success "db-init/01-init.sql created"
190fi
191
192# -- docker-compose.yml -------------------------------------------------------
193cat > docker-compose.yml <<DCEOF
194services:
195DCEOF
196
197# Cloudflare
198if $HAVE_CF || [[ "$ADD_CF" == "y" ]]; then
199 USE_CMD="$CLOUDFLARED_CMD"
200 $HAVE_CF && USE_CMD='tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}'
201cat >> docker-compose.yml <<DCEOF
202
203 # -- Cloudflare Tunnel -------------------------------------------------------
204 cloudflared:
205 image: cloudflare/cloudflared:latest
206 restart: always
207 command: $USE_CMD
208 environment:
209 - TUNNEL_TOKEN=\${TUNNEL_TOKEN}
210 networks:
211 - internal
212DCEOF
213fi
214
215# MariaDB
216NEED_DB=false
217( $HAVE_DB || $HAVE_WP1 || $HAVE_WP2 || $ADD_WP1 || $ADD_WP2 ) && NEED_DB=true
218
219if $NEED_DB; then
220cat >> docker-compose.yml <<DCEOF
221
222 # -- Database ----------------------------------------------------------------
223 db:
224 image: mariadb:10.6
225 restart: always
226 environment:
227 MYSQL_ROOT_PASSWORD: \${DB_ROOT_PASSWORD}
228 volumes:
229 - db_data:/var/lib/mysql
230 - ./db-init:/docker-entrypoint-initdb.d:ro
231 networks:
232 - internal
233 healthcheck:
234 test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
235 interval: 10s
236 timeout: 5s
237 retries: 5
238DCEOF
239fi
240
241# Uptime Kuma
242if $HAVE_KUMA || [[ "$ADD_KUMA" == "y" ]]; then
243cat >> docker-compose.yml <<DCEOF
244
245 # -- Uptime Kuma -------------------------------------------------------------
246 uptime-kuma:
247 image: louislam/uptime-kuma:latest
248 container_name: uptime-kuma
249 restart: always
250 ports:
251 - "3001:3001"
252 volumes:
253 - kuma_data:/app/data
254 networks:
255 - internal
256DCEOF
257fi
258
259# WordPress 1
260if $HAVE_WP1 || $ADD_WP1; then
261cat >> docker-compose.yml <<DCEOF
262
263 # -- WordPress 1 -------------------------------------------------------------
264 wp1:
265 image: wordpress:latest
266 restart: always
267 depends_on:
268 db:
269 condition: service_healthy
270 ports:
271 - "8081:80"
272 environment:
273 WORDPRESS_DB_HOST: db
274 WORDPRESS_DB_USER: wp1_user
275 WORDPRESS_DB_PASSWORD: \${WP1_DB_PASSWORD}
276 WORDPRESS_DB_NAME: wp1_db
277 volumes:
278 - wp1_data:/var/www/html
279 networks:
280 - internal
281DCEOF
282fi
283
284# WordPress 2
285if $HAVE_WP2 || $ADD_WP2; then
286cat >> docker-compose.yml <<DCEOF
287
288 # -- WordPress 2 -------------------------------------------------------------
289 wp2:
290 image: wordpress:latest
291 restart: always
292 depends_on:
293 db:
294 condition: service_healthy
295 ports:
296 - "8082:80"
297 environment:
298 WORDPRESS_DB_HOST: db
299 WORDPRESS_DB_USER: wp2_user
300 WORDPRESS_DB_PASSWORD: \${WP2_DB_PASSWORD}
301 WORDPRESS_DB_NAME: wp2_db
302 volumes:
303 - wp2_data:/var/www/html
304 networks:
305 - internal
306DCEOF
307fi
308
309# Networks & Volumes
310cat >> docker-compose.yml <<DCEOF
311
312networks:
313 internal:
314 driver: bridge
315
316volumes:
317DCEOF
318
319$NEED_DB && echo " db_data:" >> docker-compose.yml
320( $HAVE_KUMA || [[ "$ADD_KUMA" == "y" ]] ) && echo " kuma_data:" >> docker-compose.yml
321( $HAVE_WP1 || $ADD_WP1 ) && echo " wp1_data:" >> docker-compose.yml
322( $HAVE_WP2 || $ADD_WP2 ) && echo " wp2_data:" >> docker-compose.yml
323
324success "docker-compose.yml updated"
325
326# =============================================================================
327# Create DB users for newly added WordPress (DB is already running)
328# =============================================================================
329if $HAVE_DB; then
330 if $ADD_WP1; then
331 create_db_user "wp1_db" "wp1_user" "$WP1_DB_PASS" "$DB_ROOT_PASS" "$DB_CONTAINER"
332 fi
333 if $ADD_WP2; then
334 create_db_user "wp2_db" "wp2_user" "$WP2_DB_PASS" "$DB_ROOT_PASS" "$DB_CONTAINER"
335 fi
336fi
337
338# -- Apply changes ------------------------------------------------------------
339echo ""
340info "Prilagame promените s 'docker compose up -d' ..."
341docker compose up -d
342success "Vsichko e startиrano!"
343
344# =============================================================================
345# Summary with real URLs
346# =============================================================================
347echo ""
348echo -e "${BOLD}================================================${RESET}"
349echo -e "${BOLD} Dostap do uslugite / Access URLs ${RESET}"
350echo -e "${BOLD}================================================${RESET}"
351
352( $HAVE_WP1 || $ADD_WP1 ) && echo -e " WordPress 1 : ${GREEN}http://${SERVER_IP}:8081${RESET}"
353( $HAVE_WP2 || $ADD_WP2 ) && echo -e " WordPress 2 : ${GREEN}http://${SERVER_IP}:8082${RESET}"
354( $HAVE_KUMA || [[ "$ADD_KUMA" == "y" ]] ) && echo -e " Uptime Kuma : ${GREEN}http://${SERVER_IP}:3001${RESET}"
355( $HAVE_CF || [[ "$ADD_CF" == "y" ]] ) && echo -e " Cloudflare : ${CYAN}(configured via Cloudflare Dashboard)${RESET}"
356
357echo ""
358warn "Keep .env in a safe place - it contains your passwords!"
359echo -e "${BOLD}================================================${RESET}\n"