setup.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #!/bin/bash
  2. # Startup script for ui-apt-mirror
  3. # This script handles the deployment and configuration of the apt-mirror container
  4. set -e
  5. # Colors for output
  6. RED='\033[0;31m'
  7. GREEN='\033[0;32m'
  8. YELLOW='\033[1;33m'
  9. BLUE='\033[0;34m'
  10. NC='\033[0m' # No Color
  11. # Configuration
  12. IMAGE_NAME="ui-apt-mirror"
  13. CONTAINER_NAME="ui-apt-mirror"
  14. DIST_DIR="dist"
  15. # Function to print colored output
  16. print_status() {
  17. echo -e "${BLUE}[INFO]${NC} $1"
  18. }
  19. print_success() {
  20. echo -e "${GREEN}[SUCCESS]${NC} $1"
  21. }
  22. print_warning() {
  23. echo -e "${YELLOW}[WARNING]${NC} $1"
  24. }
  25. print_error() {
  26. echo -e "${RED}[ERROR]${NC} $1"
  27. }
  28. # Function to detect system architecture
  29. detect_architecture() {
  30. print_status "Detecting system architecture..." >&2
  31. local arch=$(uname -m)
  32. case $arch in
  33. x86_64)
  34. echo "amd64"
  35. ;;
  36. aarch64|arm64)
  37. echo "arm64"
  38. ;;
  39. *)
  40. print_error "Unsupported architecture: $arch"
  41. exit 1
  42. ;;
  43. esac
  44. }
  45. # Function to calculate optimal threads and connections based on RAM
  46. calculate_resource_limits() {
  47. print_status "Calculating optimal resource limits based on available RAM..."
  48. # Get total RAM in KB and convert to MB (fix for ARM64/OpenWrt)
  49. local total_ram_kb=$(free | awk 'NR==2{print $2}')
  50. local total_ram_mb=$((total_ram_kb / 1024))
  51. print_status "Total RAM detected: ${total_ram_mb}MB"
  52. # Calculate threads: 1 thread per 700MB RAM
  53. local calculated_threads=$((total_ram_mb / 700))
  54. # Ensure minimum of 1 thread and maximum of 8 threads
  55. if [ $calculated_threads -lt 1 ]; then
  56. calculated_threads=1
  57. elif [ $calculated_threads -gt 8 ]; then
  58. calculated_threads=8
  59. fi
  60. # Calculate connections: 6 connections per 700MB RAM
  61. local calculated_connections=$((6 * (total_ram_mb / 700)))
  62. # Ensure minimum of 6 connections and maximum of 48 connections
  63. if [ $calculated_connections -lt 6 ]; then
  64. calculated_connections=6
  65. elif [ $calculated_connections -gt 48 ]; then
  66. calculated_connections=48
  67. fi
  68. print_success "Calculated optimal settings:"
  69. print_success " - Threads: $calculated_threads (1 per 700MB RAM)"
  70. print_success " - Connections: $calculated_connections (6 per 700MB RAM)"
  71. # Set global variables
  72. OPTIMAL_THREADS=$calculated_threads
  73. OPTIMAL_CONNECTIONS=$calculated_connections
  74. }
  75. # Function to validate dist directory
  76. validate_dist() {
  77. local arch=$1
  78. local tar_file="${DIST_DIR}/${IMAGE_NAME}-${arch}.tar.gz"
  79. print_status "Validating distribution files..."
  80. if [ ! -d "$DIST_DIR" ]; then
  81. print_error "Distribution directory '$DIST_DIR' not found."
  82. print_error "Please run ./build.sh first to build the images."
  83. exit 1
  84. fi
  85. if [ ! -f "$tar_file" ]; then
  86. print_error "Image file '$tar_file' not found."
  87. print_error "Please run ./build.sh first to build the images."
  88. exit 1
  89. fi
  90. print_success "Found image file: $tar_file"
  91. }
  92. # Function to get user configuration
  93. get_user_config() {
  94. print_status "Getting user configuration..."
  95. # Default values
  96. local default_domain="mirror.intra"
  97. local default_sync_freq="14400"
  98. local default_admin_pass="admin"
  99. # Get custom domain
  100. echo ""
  101. read -p "Enter your custom domain (default: $default_domain): " custom_domain
  102. custom_domain=${custom_domain:-$default_domain}
  103. # Get sync frequency
  104. echo ""
  105. echo "Sync frequency options:"
  106. echo " 1. Every 4 hours"
  107. echo " 2. Every 12 hours"
  108. echo " 3. Every 24 hours"
  109. read -p "Select sync frequency (1-3, default: 1): " sync_choice
  110. sync_choice=${sync_choice:-1}
  111. # Convert choice to seconds
  112. case $sync_choice in
  113. 1)
  114. sync_freq="14400"
  115. ;;
  116. 2)
  117. sync_freq="43200"
  118. ;;
  119. 3)
  120. sync_freq="86400"
  121. ;;
  122. *)
  123. print_error "Invalid choice. Using default (Every 4 hours)."
  124. sync_freq="14400"
  125. ;;
  126. esac
  127. # Get admin password
  128. echo ""
  129. read -s -p "Enter admin password (default: $default_admin_pass): " admin_pass
  130. echo ""
  131. admin_pass=${admin_pass:-$default_admin_pass}
  132. # Detect host timezone
  133. local host_timezone=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC")
  134. echo ""
  135. read -p "Enter timezone (default: $host_timezone): " custom_timezone
  136. custom_timezone=${custom_timezone:-$host_timezone}
  137. # Set global variables for use in other functions
  138. MIRROR_DOMAIN="$custom_domain"
  139. ADMIN_DOMAIN="admin.$custom_domain"
  140. FILES_DOMAIN="files.$custom_domain"
  141. SYNC_FREQUENCY="$sync_freq"
  142. ADMIN_PASSWORD="$admin_pass"
  143. HOST_TIMEZONE="$custom_timezone"
  144. print_success "Configuration completed."
  145. }
  146. # Function to generate nginx htpasswd file
  147. generate_htpasswd() {
  148. local admin_pass=$1
  149. print_status "Generating nginx htpasswd file..."
  150. # Create nginx conf directory if it doesn't exist
  151. mkdir -p data/conf/nginx
  152. # Generate password hash and create htpasswd file
  153. local pass_hash=$(openssl passwd -apr1 "$admin_pass")
  154. echo "admin:$pass_hash" > data/conf/nginx/.htpasswd
  155. print_success "htpasswd file generated successfully."
  156. }
  157. # Function to generate docker-compose.yml from template
  158. generate_docker_compose() {
  159. local domain=$1
  160. local sync_freq=$2
  161. local timezone=$3
  162. print_status "Generating docker-compose.yml from template..."
  163. # Remove existing docker-compose.yml if it exists
  164. if [ -f "docker-compose.yml" ]; then
  165. print_status "Removing existing docker-compose.yml..."
  166. rm docker-compose.yml
  167. fi
  168. # Copy source template
  169. cp docker-compose.src.yml docker-compose.yml
  170. # Replace environment variable references with actual values
  171. sed -i "s/\${SYNC_FREQUENCY:-3600}/$sync_freq/g" docker-compose.yml
  172. sed -i "s/\${MIRROR_DOMAIN:-mirror.intra}/$domain/g" docker-compose.yml
  173. sed -i "s/\${ADMIN_DOMAIN:-admin.mirror.intra}/admin.$domain/g" docker-compose.yml
  174. sed -i "s/\${FILES_DOMAIN:-files.mirror.intra}/files.$domain/g" docker-compose.yml
  175. # Escape timezone for sed (replace / with \/)
  176. local escaped_timezone=$(echo "$timezone" | sed 's/\//\\\//g')
  177. sed -i "s/\${TZ:-UTC}/$escaped_timezone/g" docker-compose.yml
  178. print_success "docker-compose.yml generated successfully."
  179. }
  180. # Function to clean up previous installation
  181. cleanup_previous() {
  182. print_status "Cleaning up previous installation..."
  183. # Stop and remove existing container
  184. if docker ps -a --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
  185. print_status "Stopping existing container..."
  186. docker stop "$CONTAINER_NAME" 2>/dev/null || true
  187. print_status "Removing existing container..."
  188. docker rm "$CONTAINER_NAME" 2>/dev/null || true
  189. fi
  190. # Remove existing images
  191. if docker images --format "table {{.Repository}}" | grep -q "^${IMAGE_NAME}$"; then
  192. print_status "Removing existing images..."
  193. docker rmi "$IMAGE_NAME:latest" 2>/dev/null || true
  194. fi
  195. print_success "Cleanup completed."
  196. }
  197. # Function to create data directories
  198. create_data_dirs() {
  199. print_status "Creating data directories..."
  200. mkdir -p data/{data/apt-mirror,data/files,logs/apt-mirror,logs/nginx,conf/apt-mirror,conf/nginx/sites-available}
  201. # Set proper permissions
  202. chmod 755 data/
  203. chmod 755 data/*
  204. print_success "Data directories created."
  205. }
  206. # Function to generate apt-mirror2 configuration
  207. generate_mirror_config() {
  208. local domain=$1
  209. print_status "Generating apt-mirror2 configuration..."
  210. cat > data/conf/apt-mirror/mirror.list << EOF
  211. # apt-mirror2 configuration for $domain
  212. # Generated on $(date)
  213. # Set base_path to the directory where you want to store the mirror
  214. set base_path /var/spool/apt-mirror
  215. # Set mirror_path to the directory where you want to store the mirror
  216. set mirror_path \$base_path/mirror
  217. # Set skel_path to the directory where you want to store the skeleton
  218. set skel_path \$base_path/skel
  219. # Set var_path to the directory where you want to store the variable data
  220. set var_path \$base_path/var
  221. # Set cleanscript to the script that cleans the mirror
  222. set cleanscript \$var_path/clean.sh
  223. # Set defaultarch to the default architecture
  224. set defaultarch amd64
  225. # Set postmirror_script to the script that runs after mirroring
  226. set postmirror_script \$var_path/postmirror.sh
  227. # Set run_postmirror to 1 to run the postmirror script
  228. set run_postmirror 0
  229. # Set nthreads to the number of threads to use (calculated based on RAM)
  230. set nthreads $OPTIMAL_THREADS
  231. # Set _tilde to 1 to download tilde files
  232. set _tilde 0
  233. # Set timeout for downloads (in seconds)
  234. set _timeout 300
  235. # Set retry count for failed downloads
  236. set _retry 3
  237. # Set download speed limit (in bytes per second, 0 = unlimited)
  238. set _limit_rate 4194304
  239. # Set user agent for downloads
  240. set _user_agent "apt-mirror2/14"
  241. # Set number of connections per host (calculated based on RAM)
  242. set _max_connections $OPTIMAL_CONNECTIONS
  243. set release_files_retries 15
  244. # Ubuntu 24.04 (Noble Numbat) repositories - AMD64 architecture
  245. deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
  246. deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
  247. deb http://archive.ubuntu.com/ubuntu noble-security main restricted universe multiverse
  248. deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse
  249. # Debian 12 (Bookworm) repositories - AMD64 and ARM64 architectures
  250. deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
  251. deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
  252. deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
  253. deb http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware
  254. deb-src http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
  255. deb-src http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
  256. deb-src http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
  257. deb-src http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware
  258. # Clean up old packages
  259. clean http://archive.ubuntu.com/ubuntu
  260. clean http://deb.debian.org/debian
  261. clean http://security.debian.org/debian-security
  262. EOF
  263. print_success "apt-mirror2 configuration generated with $OPTIMAL_THREADS threads and $OPTIMAL_CONNECTIONS connections."
  264. }
  265. # Function to show status
  266. show_status() {
  267. print_status "Container status:"
  268. docker ps --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
  269. echo ""
  270. print_status "Resource Configuration:"
  271. echo " Threads: $OPTIMAL_THREADS (1 per 700MB RAM)"
  272. echo " Connections: $OPTIMAL_CONNECTIONS (6 per 700MB RAM)"
  273. echo ""
  274. print_status "Access URLs:"
  275. if [ -f "docker-compose.yml" ]; then
  276. # Extract values from generated docker-compose.yml
  277. local mirror_domain=$(grep "MIRROR_DOMAIN" docker-compose.yml | sed 's/.*MIRROR_DOMAIN: //')
  278. local admin_domain=$(grep "ADMIN_DOMAIN" docker-compose.yml | sed 's/.*ADMIN_DOMAIN: //')
  279. local files_domain=$(grep "FILES_DOMAIN" docker-compose.yml | sed 's/.*FILES_DOMAIN: //')
  280. echo " Main Repository: http://$mirror_domain"
  281. echo " Admin Panel: http://$admin_domain (admin/[password])"
  282. echo " File Repository: http://$files_domain"
  283. else
  284. echo " Main Repository: http://mirror.intra"
  285. echo " Admin Panel: http://admin.mirror.intra (admin/[password])"
  286. echo " File Repository: http://files.mirror.intra"
  287. fi
  288. echo ""
  289. print_status "Logs:"
  290. echo " docker logs $CONTAINER_NAME"
  291. echo " docker compose -f docker-compose.yml logs"
  292. }
  293. # Function to show usage
  294. show_usage() {
  295. echo "Usage: $0 [OPTIONS]"
  296. echo ""
  297. echo "Options:"
  298. echo " --config-only Only generate configuration, don't start container"
  299. echo " --no-cleanup Skip cleanup of previous installation"
  300. echo " --help Show this help message"
  301. echo ""
  302. echo "This script will:"
  303. echo " 1. Detect your system architecture"
  304. echo " 2. Calculate optimal resource limits based on available RAM"
  305. echo " 3. Validate that required image files exist"
  306. echo " 4. Get your custom configuration"
  307. echo " 5. Clean up previous installation"
  308. echo " 6. Create data directories and generate configurations"
  309. echo " 7. Call start.sh to load image and start container"
  310. echo ""
  311. echo "Resource Calculation:"
  312. echo " - Threads: 1 per 700MB RAM (min: 1, max: 8)"
  313. echo " - Connections: 6 per 700MB RAM (min: 6, max: 48)"
  314. echo ""
  315. echo "Prerequisites:"
  316. echo " - Docker installed and running"
  317. echo " - Built images in dist/ directory (run ./build.sh first)"
  318. echo " - openssl for password hashing"
  319. }
  320. # Function to update nginx configuration files with custom domain
  321. update_nginx_configs() {
  322. local domain=$1
  323. print_status "Updating nginx configuration files with domain: $domain..."
  324. # Create nginx sites-available directory if it doesn't exist
  325. mkdir -p data/conf/nginx/sites-available
  326. # Replace domain in all nginx config files
  327. for config_file in data/conf/nginx/sites-available/*.conf; do
  328. if [ -f "$config_file" ]; then
  329. # Replace mirror.intra with custom domain
  330. sed -i "s/mirror\.intra/$domain/g" "$config_file"
  331. # Replace admin.mirror.intra with admin.customdomain
  332. sed -i "s/admin\.mirror\.intra/admin.$domain/g" "$config_file"
  333. # Replace files.mirror.intra with files.customdomain
  334. sed -i "s/files\.mirror\.intra/files.$domain/g" "$config_file"
  335. fi
  336. done
  337. print_success "Nginx configuration files updated with domain: $domain"
  338. }
  339. # Main execution
  340. main() {
  341. local config_only=false
  342. local no_cleanup=false
  343. # Parse command line arguments
  344. while [[ $# -gt 0 ]]; do
  345. case $1 in
  346. --config-only)
  347. config_only=true
  348. shift
  349. ;;
  350. --no-cleanup)
  351. no_cleanup=true
  352. shift
  353. ;;
  354. --help)
  355. show_usage
  356. exit 0
  357. ;;
  358. *)
  359. print_error "Unknown option: $1"
  360. show_usage
  361. exit 1
  362. ;;
  363. esac
  364. done
  365. print_status "Starting ui-apt-mirror deployment..."
  366. # Detect architecture
  367. local arch=$(detect_architecture)
  368. print_success "Detected architecture: $arch"
  369. # Calculate optimal resource limits
  370. calculate_resource_limits
  371. # Validate dist directory
  372. validate_dist "$arch"
  373. # Get user configuration
  374. get_user_config
  375. # Generate nginx htpasswd file
  376. generate_htpasswd "$ADMIN_PASSWORD"
  377. if [ "$config_only" = true ]; then
  378. print_success "Configuration completed. Run without --config-only to start the container."
  379. exit 0
  380. fi
  381. # Clean up previous installation
  382. if [ "$no_cleanup" = false ]; then
  383. cleanup_previous
  384. fi
  385. # Create data directories
  386. create_data_dirs
  387. # Generate apt-mirror configuration
  388. generate_mirror_config "$MIRROR_DOMAIN"
  389. # Generate docker-compose.yml
  390. generate_docker_compose "$MIRROR_DOMAIN" "$SYNC_FREQUENCY" "$HOST_TIMEZONE"
  391. # Update nginx configuration files with custom domain
  392. update_nginx_configs "$MIRROR_DOMAIN"
  393. # Start container using start.sh
  394. print_status "Starting container..."
  395. ./start.sh
  396. # Show status
  397. show_status
  398. print_success "Deployment completed successfully!"
  399. }
  400. # Run main function with all arguments
  401. main "$@"