Browse Source

apt-mirror image

Denys Bashkatov 1 month ago
commit
cee5fd3c31

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+docker-compose.yml
+data/data/apt-mirror/*
+!data/data/apt-mirror/.gitkeep
+data/data/files/*
+!data/data/files/.gitkeep
+data/logs/apt-mirror/*
+!data/logs/apt-mirror/.gitkeep
+data/logs/nginx/*
+!data/logs/nginx/.gitkeep
+dist/*
+!dist/.gitkeep

+ 39 - 0
Dockerfile

@@ -0,0 +1,39 @@
+# Multi-stage build for apt-mirror with nginx
+FROM ubuntu:22.04 as base
+
+# Install system dependencies
+RUN apt-get update && apt-get install -y \
+    apt-mirror \
+    nginx \
+    openssl \
+    curl \
+    wget \
+    && rm -rf /var/lib/apt/lists/*
+
+# Create necessary directories
+RUN mkdir -p /var/spool/apt-mirror \
+    && mkdir -p /var/www/mirror.intra \
+    && mkdir -p /var/www/admin.mirror.intra \
+    && mkdir -p /var/www/files.mirror.intra \
+    && mkdir -p /etc/nginx/sites-available \
+    && mkdir -p /etc/nginx/sites-enabled
+
+# Copy scripts
+COPY scripts/ /usr/local/bin/
+RUN chmod +x /usr/local/bin/*.sh
+
+# Create symlink for apt-mirror data
+RUN ln -sf /var/spool/apt-mirror/mirror /var/www/mirror.intra/mirror
+
+# Expose ports
+EXPOSE 80 443
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
+    CMD curl -f http://localhost/ || exit 1
+
+# Start script
+COPY entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]

+ 343 - 0
README.md

@@ -0,0 +1,343 @@
+# UI APT Mirror
+
+A comprehensive APT mirror solution with a modern web interface, built with Docker. This project provides a complete local Ubuntu package repository with an intuitive admin panel and file hosting capabilities.
+
+## ๐Ÿš€ Features
+
+- **APT Mirror**: Local Ubuntu package repository with automatic synchronization
+- **Modern Web Interface**: Beautiful, responsive web UI for all services
+- **Multi-Host Setup**: Three distinct web services:
+  - `mirror.intra` - Main package repository
+  - `admin.mirror.intra` - Admin panel with authentication
+  - `files.mirror.intra` - File hosting service
+- **Multi-Architecture Support**: Builds for both AMD64 and ARM64
+- **Easy Deployment**: Simple scripts for building and deployment
+- **Configurable**: Custom domains, sync frequency, and admin passwords
+- **Health Monitoring**: Built-in health checks and monitoring
+
+## ๐Ÿ“‹ Requirements
+
+- Docker and Docker Compose
+- Linux system (tested on Ubuntu 22.04)
+- At least 10GB free disk space (for initial mirror)
+- Internet connection for initial package download
+
+## ๐Ÿ—๏ธ Architecture
+
+```
+โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
+โ”‚                    UI APT Mirror Container                  โ”‚
+โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
+โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
+โ”‚  โ”‚   nginx     โ”‚  โ”‚ apt-mirror  โ”‚  โ”‚ health-checkโ”‚        โ”‚
+โ”‚  โ”‚   (web)     โ”‚  โ”‚   (sync)    โ”‚  โ”‚  (monitor)  โ”‚        โ”‚
+โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
+โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
+โ”‚  mirror.intra  โ”‚  admin.mirror.intra  โ”‚  files.mirror.intra โ”‚
+โ”‚  (packages)    โ”‚     (admin panel)    โ”‚   (file hosting)    โ”‚
+โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
+```
+
+## ๐Ÿ› ๏ธ Installation
+
+### 1. Build the Images
+
+First, build the Docker images for your architecture:
+
+```bash
+./build.sh
+```
+
+This will create:
+- `dist/ui-apt-mirror-amd64.tar.gz` (for x86_64 systems)
+- `dist/ui-apt-mirror-arm64.tar.gz` (for ARM64 systems)
+
+### 2. Deploy the Container
+
+Run the startup script to deploy the container:
+
+```bash
+./startup.sh
+```
+
+The script will:
+- Detect your system architecture
+- Ask for your custom domain (default: `mirror.intra`)
+- Configure sync frequency
+- Set admin password
+- Load the appropriate Docker image
+- Start the container
+
+## ๐ŸŽ›๏ธ Configuration
+
+### Configuration Process
+
+The startup script uses a template-based approach:
+
+1. **Template File**: `docker-compose.src.yml` contains the base configuration with environment variable placeholders
+2. **Generation**: The startup script copies the template to `docker-compose.yml` and replaces placeholders with actual values
+3. **Customization**: You can modify the generated `docker-compose.yml` file directly if needed
+
+### Configuration Variables
+
+The following variables are configured during startup:
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `MIRROR_DOMAIN` | `mirror.intra` | Main repository domain |
+| `ADMIN_DOMAIN` | `admin.mirror.intra` | Admin panel domain |
+| `FILES_DOMAIN` | `files.mirror.intra` | File hosting domain |
+| `SYNC_FREQUENCY` | `14400` | Sync frequency in seconds (Every 4 hours) |
+| `ADMIN_PASSWORD` | `admin` | Admin panel password |
+
+**Sync Frequency Options:**
+- Every 4 hours (14400 seconds) - Default
+- Every 12 hours (43200 seconds)
+- Every 24 hours (86400 seconds)
+
+### Data Directories
+
+The following directories are automatically created and mounted:
+
+| Directory | Container Path | Purpose |
+|-----------|----------------|---------|
+| `./data/data/apt-mirror` | `/var/spool/apt-mirror` | APT mirror data |
+| `./data/logs/apt-mirror` | `/var/log` | Application logs |
+| `./data/conf/apt-mirror` | `/etc/apt` | APT mirror configuration |
+| `./data/conf/nginx/sites-available/` | `/etc/nginx/sites-available/` | Nginx configurations |
+| `./data/logs/nginx` | `/var/log/nginx` | Nginx logs |
+| `./data/data/files` | `/var/www/files.mirror.intra` | File hosting data |
+
+### APT Mirror Configuration
+
+The apt-mirror configuration is automatically generated and includes:
+- Ubuntu 24.04 (Noble Numbat) repositories
+- Debian 12 (Bookworm) repositories
+- Main, restricted, universe, and multiverse components
+- Security and updates repositories
+- Automatic cleanup of old packages
+
+### Nginx Configuration
+
+Nginx configurations are mounted as volumes from `./data/conf/nginx/` and include:
+- `nginx.conf` - Main nginx configuration
+- `mirror.intra.conf` - Main repository virtual host
+- `admin.mirror.intra.conf` - Admin panel virtual host with authentication
+- `files.mirror.intra.conf` - File hosting virtual host
+
+You can modify these configurations without rebuilding the container. Changes take effect after restarting the container.
+
+## ๐ŸŒ Web Interfaces
+
+### Main Repository (mirror.intra)
+
+- **URL**: `http://mirror.intra`
+- **Purpose**: Browse and download Ubuntu packages
+- **Features**:
+  - Package browsing with directory listing
+  - Download statistics
+  - Usage instructions
+  - Quick links to other services
+
+### Admin Panel (admin.mirror.intra)
+
+- **URL**: `http://admin.mirror.intra`
+- **Authentication**: Basic auth (admin/password)
+- **Features**:
+  - Mirror status monitoring
+  - Sync controls (start/stop)
+  - Statistics and metrics
+  - Log viewing
+  - Configuration management
+
+### File Repository (files.mirror.intra)
+
+- **URL**: `http://files.mirror.intra`
+- **Purpose**: File hosting and sharing
+- **Features**:
+  - Drag-and-drop file upload
+  - File browsing with icons
+  - Download statistics
+  - Disk usage monitoring
+
+## ๐Ÿ“Š Usage
+
+### Using the APT Mirror
+
+To use the local repository on your Ubuntu systems, add the following to `/etc/apt/sources.list`:
+
+```bash
+# Replace mirror.intra with your custom domain
+# For Ubuntu 24.04 (Noble)
+deb http://mirror.intra/ubuntu/ noble main restricted universe multiverse
+deb http://mirror.intra/ubuntu/ noble-updates main restricted universe multiverse
+deb http://mirror.intra/ubuntu/ noble-security main restricted universe multiverse
+
+# For Debian 12 (Bookworm)
+deb http://mirror.intra/debian/ bookworm main contrib non-free non-free-firmware
+deb http://mirror.intra/debian/ bookworm-updates main contrib non-free non-free-firmware
+deb http://mirror.intra/debian-security bookworm-security main contrib non-free non-free-firmware
+```
+
+Then update your package lists:
+
+```bash
+sudo apt update
+```
+
+### Managing the Mirror
+
+Access the admin panel at `http://admin.mirror.intra` to:
+- Monitor sync status
+- Start/stop synchronization
+- View logs and statistics
+- Manage configuration
+
+### File Hosting
+
+Use the file repository at `http://files.mirror.intra` to:
+- Upload files via web interface
+- Browse uploaded files
+- Download files
+- Monitor disk usage
+
+## ๐Ÿ”ง Management
+
+### Viewing Logs
+
+```bash
+# Container logs
+docker logs ui-apt-mirror
+
+# Docker Compose logs
+docker compose -f docker-compose.yml logs
+
+# Specific service logs
+docker compose -f docker-compose.yml logs ui-apt-mirror
+```
+
+### Stopping the Container
+
+```bash
+docker compose -f docker-compose.yml down
+```
+
+### Restarting the Container
+
+```bash
+docker compose -f docker-compose.yml restart
+```
+
+### Updating Configuration
+
+To update the configuration:
+
+1. **Automatic Method**: Run `./startup.sh` again to regenerate configuration
+2. **Manual Method**: 
+   - Stop the container: `docker compose -f docker-compose.yml down`
+   - Edit the generated `docker-compose.yml` file directly
+   - Restart the container: `docker compose -f docker-compose.yml up -d`
+
+**Note**: The `.env` file is used internally by the startup script. For manual changes, edit `docker-compose.yml` directly.
+
+## ๐Ÿ“ Directory Structure
+
+```
+ui-apt-mirror/
+โ”œโ”€โ”€ build.sh                 # Build script for Docker images
+โ”œโ”€โ”€ startup.sh               # Deployment and configuration script
+โ”œโ”€โ”€ README.md               # This file
+โ”œโ”€โ”€ .env                    # Configuration file (generated)
+โ”œโ”€โ”€ docker-compose.src.yml  # Docker Compose template
+โ”œโ”€โ”€ docker-compose.yml      # Generated Docker Compose file
+โ”œโ”€โ”€ dist/                   # Built Docker images
+โ”‚   โ”œโ”€โ”€ ui-apt-mirror-amd64.tar.gz
+โ”‚   โ””โ”€โ”€ ui-apt-mirror-arm64.tar.gz
+โ”œโ”€โ”€ Dockerfile              # Multi-stage Docker build
+โ”œโ”€โ”€ entrypoint.sh           # Container startup script
+โ”œโ”€โ”€ scripts/                # Service scripts
+โ””โ”€โ”€ web/                    # Web content
+    โ”œโ”€โ”€ mirror.intra/
+    โ”œโ”€โ”€ admin.mirror.intra/
+    โ””โ”€โ”€ files.mirror.intra/
+โ””โ”€โ”€ data/                   # Persistent data and configuration
+    โ”œโ”€โ”€ data/apt-mirror/    # APT mirror data
+    โ”œโ”€โ”€ data/files/         # File hosting data
+    โ”œโ”€โ”€ logs/apt-mirror/    # Application logs
+    โ”œโ”€โ”€ logs/nginx/         # Nginx logs
+    โ”œโ”€โ”€ conf/apt-mirror/    # APT mirror configuration
+    โ””โ”€โ”€ conf/nginx/         # Nginx configurations
+```
+
+## ๐Ÿ” Troubleshooting
+
+### Common Issues
+
+1. **Port 80 already in use**
+   ```bash
+   # Check what's using port 80
+   sudo netstat -tlnp | grep :80
+   
+   # Stop conflicting service or change port in docker-compose.yml
+   ```
+
+2. **Permission denied errors**
+   ```bash
+   # Fix data directory permissions
+   sudo chown -R $USER:$USER data/
+   ```
+
+3. **Container won't start**
+   ```bash
+   # Check container logs
+   docker logs ui-apt-mirror
+   
+   # Check if image was loaded correctly
+   docker images | grep ui-apt-mirror
+   ```
+
+4. **Sync not working**
+   ```bash
+   # Check apt-mirror logs
+   docker exec ui-apt-mirror cat /var/log/apt-mirror.log
+   
+   # Check configuration
+   docker exec ui-apt-mirror cat /etc/apt/mirror.list
+   ```
+
+### Health Checks
+
+The container includes built-in health checks. Check the status:
+
+```bash
+docker inspect ui-apt-mirror | grep -A 10 "Health"
+```
+
+## ๐Ÿค Contributing
+
+1. Fork the repository
+2. Create a feature branch
+3. Make your changes
+4. Test thoroughly
+5. Submit a pull request
+
+## ๐Ÿ“„ License
+
+This project is licensed under the MIT License - see the LICENSE file for details.
+
+## ๐Ÿ™ Acknowledgments
+
+- [apt-mirror](https://github.com/apt-mirror/apt-mirror) - The APT mirroring tool
+- [nginx](https://nginx.org/) - Web server
+- [Docker](https://docker.com/) - Containerization platform
+
+## ๐Ÿ“ž Support
+
+For issues and questions:
+1. Check the troubleshooting section
+2. Review the logs
+3. Open an issue on GitHub
+
+---
+
+**Note**: This is a development tool. For production use, ensure proper security measures are in place, including HTTPS, firewall rules, and regular security updates. 

+ 228 - 0
build.sh

@@ -0,0 +1,228 @@
+#!/bin/bash
+
+# Build script for ui-apt-mirror
+# This script builds Docker images for multiple architectures and saves them to dist/
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Configuration
+IMAGE_NAME="ui-apt-mirror"
+VERSION="latest"
+DIST_DIR="dist"
+
+# Function to print colored output
+print_status() {
+    echo -e "${BLUE}[INFO]${NC} $1"
+}
+
+print_success() {
+    echo -e "${GREEN}[SUCCESS]${NC} $1"
+}
+
+print_warning() {
+    echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+print_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Function to check prerequisites
+check_prerequisites() {
+    print_status "Checking prerequisites..."
+    
+    # Check if Docker is installed and running
+    if ! command -v docker &> /dev/null; then
+        print_error "Docker is not installed. Please install Docker first."
+        exit 1
+    fi
+    
+    if ! docker info &> /dev/null; then
+        print_error "Docker is not running. Please start Docker first."
+        exit 1
+    fi
+    
+    # Check if Docker Buildx is available
+    if ! docker buildx version &> /dev/null; then
+        print_warning "Docker Buildx not available. Installing..."
+        docker buildx install
+    fi
+    
+    # Check if Dockerfile exists
+    if [ ! -f "Dockerfile" ]; then
+        print_error "Dockerfile not found in current directory."
+        exit 1
+    fi
+    
+    # Create dist directory if it doesn't exist
+    mkdir -p "$DIST_DIR"
+    
+    print_success "Prerequisites check completed."
+}
+
+# Function to create multi-platform builder
+setup_builder() {
+    print_status "Setting up multi-platform builder..."
+    
+    # Create a new builder instance if it doesn't exist
+    if ! docker buildx inspect multiarch &> /dev/null; then
+        docker buildx create --name multiarch --use
+    else
+        docker buildx use multiarch
+    fi
+    
+    # Bootstrap the builder
+    docker buildx inspect --bootstrap
+    
+    print_success "Multi-platform builder setup completed."
+}
+
+# Function to build images for all architectures
+build_images() {
+    print_status "Building images for all architectures..."
+    
+    # Define architectures
+    ARCHITECTURES=("linux/amd64" "linux/arm64")
+    
+    # Build for each architecture
+    for arch in "${ARCHITECTURES[@]}"; do
+        arch_short=$(echo "$arch" | sed 's/linux\///')
+        print_status "Building for $arch..."
+        
+        # Build the image
+        docker buildx build \
+            --platform "$arch" \
+            --tag "${IMAGE_NAME}:${VERSION}" \
+            --file "Dockerfile" \
+            --load \
+            .
+        
+        # Save the image to tar file
+        output_file="${DIST_DIR}/${IMAGE_NAME}-${arch_short}.tar"
+        print_status "Saving image to $output_file..."
+        
+        docker save "${IMAGE_NAME}:${VERSION}" -o "$output_file"
+        
+        # Compress the tar file
+        print_status "Compressing $output_file..."
+        gzip -f "$output_file"
+        
+        print_success "Built and saved ${IMAGE_NAME}-${arch_short}.tar.gz"
+    done
+}
+
+# Function to build and push to registry (optional)
+build_and_push() {
+    if [ "$1" = "--push" ]; then
+        print_status "Building and pushing to registry..."
+        
+        # Check if registry is specified
+        if [ -z "$REGISTRY" ]; then
+            print_error "REGISTRY environment variable not set. Skipping push."
+            return
+        fi
+        
+        # Build and push for all architectures
+        docker buildx build \
+            --platform linux/amd64,linux/arm64 \
+            --tag "${REGISTRY}/${IMAGE_NAME}:${VERSION}" \
+            --file "Dockerfile" \
+            --push \
+            .
+        
+        print_success "Images pushed to registry: ${REGISTRY}/${IMAGE_NAME}:${VERSION}"
+    fi
+}
+
+# Function to clean up
+cleanup() {
+    print_status "Cleaning up..."
+    
+    # Remove the built image
+    docker rmi "${IMAGE_NAME}:${VERSION}" 2>/dev/null || true
+    
+    print_success "Cleanup completed."
+}
+
+# Function to show usage
+show_usage() {
+    echo "Usage: $0 [OPTIONS]"
+    echo ""
+    echo "Options:"
+    echo "  --push              Build and push to registry (requires REGISTRY env var)"
+    echo "  --clean             Clean up built images after saving"
+    echo "  --help              Show this help message"
+    echo ""
+    echo "Environment variables:"
+    echo "  REGISTRY            Docker registry URL (e.g., docker.io/username)"
+    echo "  VERSION             Image version (default: latest)"
+    echo ""
+    echo "Examples:"
+    echo "  $0                  # Build and save images locally"
+    echo "  $0 --push           # Build and push to registry"
+    echo "  REGISTRY=docker.io/username $0 --push  # Build and push to specific registry"
+}
+
+# Main execution
+main() {
+    local push_flag=false
+    local clean_flag=false
+    
+    # Parse command line arguments
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            --push)
+                push_flag=true
+                shift
+                ;;
+            --clean)
+                clean_flag=true
+                shift
+                ;;
+            --help)
+                show_usage
+                exit 0
+                ;;
+            *)
+                print_error "Unknown option: $1"
+                show_usage
+                exit 1
+                ;;
+        esac
+    done
+    
+    print_status "Starting build process for ui-apt-mirror..."
+    
+    # Check prerequisites
+    check_prerequisites
+    
+    # Setup builder
+    setup_builder
+    
+    # Build images
+    build_images
+    
+    # Push to registry if requested
+    if [ "$push_flag" = true ]; then
+        build_and_push --push
+    fi
+    
+    # Cleanup if requested
+    if [ "$clean_flag" = true ]; then
+        cleanup
+    fi
+    
+    print_success "Build process completed successfully!"
+    print_status "Images saved to $DIST_DIR/:"
+    ls -la "$DIST_DIR"/*.tar.gz 2>/dev/null || print_warning "No tar files found in $DIST_DIR"
+}
+
+# Run main function with all arguments
+main "$@" 

+ 49 - 0
data/conf/apt-mirror/mirror.list

@@ -0,0 +1,49 @@
+# apt-mirror configuration for ui-apt-mirror
+# Generated on $(date)
+
+# Set base_path to the directory where you want to store the mirror
+set base_path    /var/spool/apt-mirror
+
+# Set mirror_path to the directory where you want to store the mirror
+set mirror_path  $base_path/mirror
+
+# Set skel_path to the directory where you want to store the skeleton
+set skel_path    $base_path/skel
+
+# Set var_path to the directory where you want to store the variable data
+set var_path     $base_path/var
+
+# Set cleanscript to the script that cleans the mirror
+set cleanscript  $var_path/clean.sh
+
+# Set defaultarch to the default architecture
+set defaultarch  amd64
+
+# Set postmirror_script to the script that runs after mirroring
+set postmirror_script $var_path/postmirror.sh
+
+# Set run_postmirror to 1 to run the postmirror script
+set run_postmirror 0
+
+# Set nthreads to the number of threads to use
+set nthreads     20
+
+# Set _tilde to 1 to download tilde files
+set _tilde 0
+
+# Ubuntu 24.04 (Noble Numbat) repositories
+deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-security main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse
+
+# Debian 12 (Bookworm) repositories
+deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
+deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
+deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
+deb http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware
+
+# Clean up old packages
+clean http://archive.ubuntu.com/ubuntu
+clean http://deb.debian.org/debian
+clean http://security.debian.org/debian-security 

+ 20 - 0
data/conf/nginx/sites-available/admin.mirror.intra.conf

@@ -0,0 +1,20 @@
+server {
+    listen 80;
+    server_name admin.mirror.intra;
+
+    root /var/www/admin.mirror.intra;
+    index index.html index.htm;
+
+    # Basic authentication
+    auth_basic "Admin Area";
+    auth_basic_user_file /etc/nginx/.htpasswd;
+
+    # Admin interface
+    location / {
+        try_files $uri $uri/ =404;
+    }
+
+    # Logging
+    access_log /var/log/nginx/admin.mirror.intra.access.log;
+    error_log /var/log/nginx/admin.mirror.intra.error.log;
+} 

+ 25 - 0
data/conf/nginx/sites-available/files.mirror.intra.conf

@@ -0,0 +1,25 @@
+server {
+    listen 80;
+    server_name files.mirror.intra;
+
+    root /var/www/files.mirror.intra;
+
+    # File serving with directory listing
+    location / {
+        autoindex on;
+        autoindex_exact_size off;
+        autoindex_localtime on;
+        try_files $uri $uri/ =404;
+    }
+
+    # Large file downloads
+    location ~* \.(iso|tar\.gz|tar\.xz|zip|deb)$ {
+        expires 1d;
+        add_header Cache-Control "public";
+        client_max_body_size 0;
+    }
+
+    # Logging
+    access_log /var/log/nginx/files.mirror.intra.access.log;
+    error_log /var/log/nginx/files.mirror.intra.error.log;
+} 

+ 22 - 0
data/conf/nginx/sites-available/mirror.intra.conf

@@ -0,0 +1,22 @@
+server {
+    listen 80;
+    server_name mirror.intra;
+
+    # Serve apt-mirror files
+    location / {
+        alias /var/spool/apt-mirror/mirror/;
+        autoindex on;
+        autoindex_exact_size off;
+        autoindex_localtime on;
+        
+        # Cache static files
+        location ~* \.(deb|dsc|tar\.gz|tar\.xz|tar\.bz2)$ {
+            expires 1y;
+            add_header Cache-Control "public, immutable";
+        }
+    }
+
+    # Logging
+    access_log /var/log/nginx/mirror.intra.access.log;
+    error_log /var/log/nginx/mirror.intra.error.log;
+} 

+ 0 - 0
data/data/apt-mirror/.gitkeep


+ 0 - 0
data/data/files/.gitkeep


+ 0 - 0
data/logs/apt-mirror/.gitkeep


+ 0 - 0
data/logs/nginx/.gitkeep


+ 0 - 0
dist/.gitkeep


+ 37 - 0
docker-compose.src.yml

@@ -0,0 +1,37 @@
+services:
+  ui-apt-mirror:
+    image: ui-apt-mirror:latest
+    container_name: ui-apt-mirror
+    restart: unless-stopped
+    ports:
+      - "80:80"
+    volumes:
+      - ./data/data/apt-mirror:/var/spool/apt-mirror
+      - ./data/logs/apt-mirror:/var/log
+      - ./data/conf/apt-mirror:/etc/apt
+      - ./data/conf/nginx/sites-available/mirror.intra.conf:/etc/nginx/sites-available/mirror.intra.conf
+      - ./data/conf/nginx/sites-available/admin.mirror.intra.conf:/etc/nginx/sites-available/admin.mirror.intra.conf
+      - ./data/conf/nginx/sites-available/files.mirror.intra.conf:/etc/nginx/sites-available/files.mirror.intra.conf
+      - ./data/logs/nginx:/var/log/nginx
+      - ./data/data/files:/var/www/files.mirror.intra
+    environment:
+      - SYNC_FREQUENCY=${SYNC_FREQUENCY:-3600}
+      - MIRROR_DOMAIN=${MIRROR_DOMAIN:-mirror.intra}
+      - ADMIN_DOMAIN=${ADMIN_DOMAIN:-admin.mirror.intra}
+      - FILES_DOMAIN=${FILES_DOMAIN:-files.mirror.intra}
+      - ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
+    networks:
+      - mirror_network
+    healthcheck:
+      test: ["CMD", "/usr/local/bin/health-check.sh", "once"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
+      start_period: 40s
+    labels:
+      - "com.docker.compose.project=ui-apt-mirror"
+      - "com.docker.compose.service=mirror"
+
+networks:
+  mirror_network:
+    driver: bridge

+ 99 - 0
entrypoint.sh

@@ -0,0 +1,99 @@
+#!/bin/bash
+set -e
+
+echo "๐Ÿš€ Starting APT Mirror Container..."
+
+# Function to handle shutdown gracefully
+cleanup() {
+    echo "๐Ÿ›‘ Shutting down services..."
+    if [ -n "$NGINX_PID" ]; then
+        kill $NGINX_PID
+    fi
+    if [ -n "$MIRROR_PID" ]; then
+        kill $MIRROR_PID
+    fi
+    exit 0
+}
+
+# Set up signal handlers
+trap cleanup SIGTERM SIGINT
+
+# Create necessary directories if they don't exist
+mkdir -p /var/log/nginx
+mkdir -p /var/spool/apt-mirror
+mkdir -p /var/www/mirror.intra
+
+# Set proper permissions
+chown -R www-data:www-data /var/www
+chown -R www-data:www-data /var/spool/apt-mirror
+
+# Create symlink for apt-mirror data if it doesn't exist
+if [ ! -L /var/www/mirror.intra/mirror ]; then
+    ln -sf /var/spool/apt-mirror/mirror /var/www/mirror.intra/mirror
+fi
+
+# Enable nginx sites if configs are mounted
+if [ -f /etc/nginx/sites-available/mirror.intra.conf ]; then
+    echo "๐Ÿ”— Enabling nginx sites..."
+    # Remove default site if it exists
+    rm -f /etc/nginx/sites-enabled/default
+    ln -sf /etc/nginx/sites-available/mirror.intra.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
+    ln -sf /etc/nginx/sites-available/admin.mirror.intra.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
+    ln -sf /etc/nginx/sites-available/files.mirror.intra.conf /etc/nginx/sites-enabled/ 2>/dev/null || true
+    echo "โœ… Nginx sites enabled"
+else
+    echo "โš ๏ธ  Nginx configuration not found. Please ensure nginx config volume is mounted."
+fi
+
+# Generate basic auth file if not exists
+if [ ! -f /etc/nginx/.htpasswd ]; then
+    echo "๐Ÿ” Generating default admin password..."
+    echo "admin:\$apr1\$rqx1VHAW\$ba.dgiFvpCyUhWZkkNPwT0" > /etc/nginx/.htpasswd
+    echo "Default admin credentials: admin/admin"
+fi
+
+# Start nginx
+echo "๐ŸŒ Starting nginx..."
+nginx -g "daemon off;" &
+NGINX_PID=$!
+
+# Wait a moment for nginx to start
+sleep 2
+
+# Check if nginx started successfully
+if ! kill -0 $NGINX_PID 2>/dev/null; then
+    echo "โŒ Failed to start nginx"
+    exit 1
+fi
+
+echo "โœ… nginx started successfully (PID: $NGINX_PID)"
+
+# Start apt-mirror sync in background if configuration exists
+if [ -f /etc/apt/mirror.list ]; then
+    echo "๐Ÿ”„ Starting apt-mirror sync..."
+    /usr/local/bin/mirror-sync.sh &
+    MIRROR_PID=$!
+    echo "โœ… apt-mirror sync started (PID: $MIRROR_PID)"
+else
+    echo "โš ๏ธ  No apt-mirror configuration found. Skipping sync."
+fi
+
+# Start health check service
+echo "๐Ÿฅ Starting health check service..."
+/usr/local/bin/health-check.sh &
+HEALTH_PID=$!
+
+echo "๐ŸŽ‰ All services started successfully!"
+echo "๐Ÿ“Š Services running:"
+echo "   - nginx (PID: $NGINX_PID)"
+if [ -n "$MIRROR_PID" ]; then
+    echo "   - apt-mirror sync (PID: $MIRROR_PID)"
+fi
+echo "   - health check (PID: $HEALTH_PID)"
+
+# Wait for any process to exit
+wait
+
+# If we get here, something went wrong
+echo "โŒ One of the services exited unexpectedly"
+exit 1 

+ 185 - 0
scripts/health-check.sh

@@ -0,0 +1,185 @@
+#!/bin/bash
+
+# Health check script for apt-mirror container
+# This script monitors the health of nginx and apt-mirror services
+
+HEALTH_LOG="/var/log/health-check.log"
+NGINX_PID_FILE="/var/run/nginx.pid"
+MIRROR_LOCK_FILE="/var/run/apt-mirror.lock"
+HEALTH_STATUS_FILE="/var/run/health.status"
+
+# Function to log health check messages
+log_health() {
+    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$HEALTH_LOG"
+}
+
+# Function to check nginx status
+check_nginx() {
+    if [ -f "$NGINX_PID_FILE" ]; then
+        local pid=$(cat "$NGINX_PID_FILE")
+        if kill -0 "$pid" 2>/dev/null; then
+            # Check if nginx is responding
+            if curl -f -s http://localhost/ > /dev/null 2>&1; then
+                echo "nginx:running:$pid"
+                return 0
+            else
+                echo "nginx:not_responding:$pid"
+                return 1
+            fi
+        else
+            echo "nginx:not_running:"
+            return 1
+        fi
+    else
+        echo "nginx:no_pid_file:"
+        return 1
+    fi
+}
+
+# Function to check apt-mirror status
+check_mirror() {
+    if [ -f "$MIRROR_LOCK_FILE" ]; then
+        local pid=$(cat "$MIRROR_LOCK_FILE")
+        if kill -0 "$pid" 2>/dev/null; then
+            echo "mirror:syncing:$pid"
+            return 0
+        else
+            echo "mirror:stale_lock:$pid"
+            return 1
+        fi
+    else
+        echo "mirror:idle:"
+        return 0
+    fi
+}
+
+# Function to check disk usage
+check_disk() {
+    local usage=$(df /var/spool/apt-mirror | tail -1 | awk '{print $5}' | sed 's/%//')
+    echo "disk:$usage%"
+    if [ "$usage" -gt 90 ]; then
+        return 1
+    fi
+    return 0
+}
+
+# Function to check last sync time
+check_last_sync() {
+    if [ -f "/var/spool/apt-mirror/last-sync.txt" ]; then
+        local last_sync=$(cat "/var/spool/apt-mirror/last-sync.txt")
+        echo "last_sync:$last_sync"
+    else
+        echo "last_sync:never"
+    fi
+}
+
+# Function to get system uptime
+get_uptime() {
+    local uptime=$(uptime -p 2>/dev/null || echo "unknown")
+    echo "uptime:$uptime"
+}
+
+# Function to get memory usage
+get_memory() {
+    local mem_info=$(free -m | grep Mem)
+    local total=$(echo $mem_info | awk '{print $2}')
+    local used=$(echo $mem_info | awk '{print $3}')
+    local usage=$((used * 100 / total))
+    echo "memory:${usage}%"
+}
+
+# Function to perform comprehensive health check
+do_health_check() {
+    local status="healthy"
+    local details=()
+    
+    # Check nginx
+    local nginx_status=$(check_nginx)
+    details+=("nginx:$nginx_status")
+    if ! echo "$nginx_status" | grep -q "running"; then
+        status="unhealthy"
+    fi
+    
+    # Check mirror
+    local mirror_status=$(check_mirror)
+    details+=("mirror:$mirror_status")
+    
+    # Check disk
+    local disk_status=$(check_disk)
+    details+=("disk:$disk_status")
+    if echo "$disk_status" | grep -q "9[0-9]%\|100%"; then
+        status="warning"
+    fi
+    
+    # Get additional info
+    details+=("$(check_last_sync)")
+    details+=("$(get_uptime)")
+    details+=("$(get_memory)")
+    
+    # Write status to file
+    cat > "$HEALTH_STATUS_FILE" << EOF
+{
+    "status": "$status",
+    "timestamp": "$(date -Iseconds)",
+    "details": {
+        $(printf '%s\n' "${details[@]}" | sed 's/:/": "/; s/$/"/; s/^/        "/; s/:/": "/')
+    }
+}
+EOF
+    
+    echo "$status"
+}
+
+# Function to run continuous monitoring
+run_monitoring() {
+    log_health "Starting health monitoring"
+    
+    while true; do
+        local health_status=$(do_health_check)
+        log_health "Health check result: $health_status"
+        
+        # Sleep for 30 seconds before next check
+        sleep 30
+    done
+}
+
+# Function to run single health check
+run_once() {
+    local health_status=$(do_health_check)
+    echo "$health_status"
+    
+    if [ "$health_status" = "healthy" ]; then
+        exit 0
+    else
+        exit 1
+    fi
+}
+
+# Function to get detailed status
+get_status() {
+    if [ -f "$HEALTH_STATUS_FILE" ]; then
+        cat "$HEALTH_STATUS_FILE"
+    else
+        echo '{"status": "unknown", "timestamp": "", "details": {}}'
+    fi
+}
+
+# Main execution
+case "${1:-once}" in
+    "once")
+        run_once
+        ;;
+    "monitor")
+        run_monitoring
+        ;;
+    "status")
+        get_status
+        ;;
+    *)
+        echo "Usage: $0 {once|monitor|status}"
+        echo "  once    - Run health check once and exit"
+        echo "  monitor - Run continuous health monitoring"
+        echo "  status  - Get detailed status information"
+        exit 1
+        ;;
+esac 

+ 125 - 0
scripts/mirror-sync.sh

@@ -0,0 +1,125 @@
+#!/bin/bash
+
+# apt-mirror sync script
+# This script handles the synchronization of apt repositories
+
+MIRROR_CONFIG="/etc/apt/mirror.list"
+MIRROR_LOG="/var/log/apt-mirror.log"
+SYNC_FREQUENCY="${SYNC_FREQUENCY:-3600}"  # Default: 1 hour
+LOCK_FILE="/var/run/apt-mirror.lock"
+
+# Function to log messages
+log() {
+    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$MIRROR_LOG"
+}
+
+# Function to check if sync is already running
+check_lock() {
+    if [ -f "$LOCK_FILE" ]; then
+        local pid=$(cat "$LOCK_FILE" 2>/dev/null)
+        if kill -0 "$pid" 2>/dev/null; then
+            log "Sync already running (PID: $pid)"
+            return 1
+        else
+            log "Removing stale lock file"
+            rm -f "$LOCK_FILE"
+        fi
+    fi
+    return 0
+}
+
+# Function to create lock file
+create_lock() {
+    echo $$ > "$LOCK_FILE"
+}
+
+# Function to remove lock file
+remove_lock() {
+    rm -f "$LOCK_FILE"
+}
+
+# Function to perform sync
+do_sync() {
+    log "Starting apt-mirror sync..."
+    
+    if [ ! -f "$MIRROR_CONFIG" ]; then
+        log "ERROR: Mirror configuration not found at $MIRROR_CONFIG"
+        return 1
+    fi
+    
+    # Create lock file
+    create_lock
+    
+    # Run apt-mirror
+    if apt-mirror "$MIRROR_CONFIG" 2>&1 | tee -a "$MIRROR_LOG"; then
+        log "Sync completed successfully"
+        
+        # Update symlink to ensure web server sees latest data
+        if [ -d "/var/spool/apt-mirror/mirror" ]; then
+            ln -sf /var/spool/apt-mirror/mirror /var/www/mirror.intra/mirror
+            log "Updated web symlink"
+        fi
+        
+        # Update last sync timestamp
+        date > /var/spool/apt-mirror/last-sync.txt
+    else
+        log "ERROR: Sync failed"
+        remove_lock
+        return 1
+    fi
+    
+    remove_lock
+    return 0
+}
+
+# Function to run continuous sync
+run_continuous() {
+    log "Starting continuous sync with frequency: ${SYNC_FREQUENCY}s"
+    
+    while true; do
+        if check_lock; then
+            do_sync
+        fi
+        
+        log "Waiting ${SYNC_FREQUENCY} seconds until next sync..."
+        sleep "$SYNC_FREQUENCY"
+    done
+}
+
+# Function to run single sync
+run_once() {
+    if check_lock; then
+        do_sync
+    else
+        exit 1
+    fi
+}
+
+# Main execution
+case "${1:-continuous}" in
+    "once")
+        run_once
+        ;;
+    "continuous")
+        run_continuous
+        ;;
+    "status")
+        if [ -f "$LOCK_FILE" ]; then
+            local pid=$(cat "$LOCK_FILE")
+            if kill -0 "$pid" 2>/dev/null; then
+                echo "Sync running (PID: $pid)"
+            else
+                echo "Stale lock file found"
+            fi
+        else
+            echo "No sync running"
+        fi
+        ;;
+    *)
+        echo "Usage: $0 {once|continuous|status}"
+        echo "  once        - Run sync once and exit"
+        echo "  continuous  - Run sync continuously (default)"
+        echo "  status      - Check sync status"
+        exit 1
+        ;;
+esac 

+ 422 - 0
startup.sh

@@ -0,0 +1,422 @@
+#!/bin/bash
+
+# Startup script for ui-apt-mirror
+# This script handles the deployment and configuration of the apt-mirror container
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Configuration
+IMAGE_NAME="ui-apt-mirror"
+CONTAINER_NAME="ui-apt-mirror"
+DIST_DIR="dist"
+CONFIG_FILE=".env"
+
+# Function to print colored output
+print_status() {
+    echo -e "${BLUE}[INFO]${NC} $1"
+}
+
+print_success() {
+    echo -e "${GREEN}[SUCCESS]${NC} $1"
+}
+
+print_warning() {
+    echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+print_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Function to detect system architecture
+detect_architecture() {
+    print_status "Detecting system architecture..." >&2
+    
+    local arch=$(uname -m)
+    case $arch in
+        x86_64)
+            echo "amd64"
+            ;;
+        aarch64|arm64)
+            echo "arm64"
+            ;;
+        *)
+            print_error "Unsupported architecture: $arch"
+            exit 1
+            ;;
+    esac
+}
+
+# Function to validate dist directory
+validate_dist() {
+    local arch=$1
+    local tar_file="${DIST_DIR}/${IMAGE_NAME}-${arch}.tar.gz"
+    
+    print_status "Validating distribution files..."
+    
+    if [ ! -d "$DIST_DIR" ]; then
+        print_error "Distribution directory '$DIST_DIR' not found."
+        print_error "Please run ./build.sh first to build the images."
+        exit 1
+    fi
+    
+    if [ ! -f "$tar_file" ]; then
+        print_error "Image file '$tar_file' not found."
+        print_error "Please run ./build.sh first to build the images."
+        exit 1
+    fi
+    
+    print_success "Found image file: $tar_file"
+}
+
+# Function to get user configuration
+get_user_config() {
+    print_status "Getting user configuration..."
+    
+    # Default values
+    local default_domain="mirror.intra"
+    local default_sync_freq="14400"
+    local default_admin_pass="admin"
+    
+    # Get custom domain
+    echo ""
+    read -p "Enter your custom domain (default: $default_domain): " custom_domain
+    custom_domain=${custom_domain:-$default_domain}
+    
+    # Get sync frequency
+    echo ""
+    echo "Sync frequency options:"
+    echo "  1. Every 4 hours"
+    echo "  2. Every 12 hours"
+    echo "  3. Every 24 hours"
+    read -p "Select sync frequency (1-3, default: 1): " sync_choice
+    sync_choice=${sync_choice:-1}
+    
+    # Convert choice to seconds
+    case $sync_choice in
+        1)
+            sync_freq="14400"
+            ;;
+        2)
+            sync_freq="43200"
+            ;;
+        3)
+            sync_freq="86400"
+            ;;
+        *)
+            print_error "Invalid choice. Using default (Every 4 hours)."
+            sync_freq="14400"
+            ;;
+    esac
+    
+    # Get admin password
+    echo ""
+    read -s -p "Enter admin password (default: $default_admin_pass): " admin_pass
+    echo ""
+    admin_pass=${admin_pass:-$default_admin_pass}
+    
+    # Generate admin password hash
+    print_status "Generating admin password hash..."
+    local pass_hash=$(openssl passwd -apr1 "$admin_pass")
+    
+    # Create environment configuration
+    cat > "$CONFIG_FILE" << EOF
+# ui-apt-mirror Configuration
+MIRROR_DOMAIN=$custom_domain
+ADMIN_DOMAIN=admin.$custom_domain
+FILES_DOMAIN=files.$custom_domain
+SYNC_FREQUENCY=$sync_freq
+ADMIN_PASSWORD=$admin_pass
+ADMIN_PASSWORD_HASH=$pass_hash
+EOF
+    
+    print_success "Configuration saved to $CONFIG_FILE"
+}
+
+# Function to generate docker-compose.yml from template
+generate_docker_compose() {
+    local domain=$1
+    local sync_freq=$2
+    local admin_pass=$3
+    
+    print_status "Generating docker-compose.yml from template..."
+    
+    # Remove existing docker-compose.yml if it exists
+    if [ -f "docker-compose.yml" ]; then
+        print_status "Removing existing docker-compose.yml..."
+        rm docker-compose.yml
+    fi
+    
+    # Copy source template
+    cp docker-compose.src.yml docker-compose.yml
+    
+    # Replace environment variable references with actual values
+    sed -i "s/\${SYNC_FREQUENCY:-3600}/$sync_freq/g" docker-compose.yml
+    sed -i "s/\${MIRROR_DOMAIN:-mirror.intra}/$domain/g" docker-compose.yml
+    sed -i "s/\${ADMIN_DOMAIN:-admin.mirror.intra}/admin.$domain/g" docker-compose.yml
+    sed -i "s/\${FILES_DOMAIN:-files.mirror.intra}/files.$domain/g" docker-compose.yml
+    sed -i "s/\${ADMIN_PASSWORD:-admin}/$admin_pass/g" docker-compose.yml
+    
+    print_success "docker-compose.yml generated successfully."
+}
+
+# Function to clean up previous installation
+cleanup_previous() {
+    print_status "Cleaning up previous installation..."
+    
+    # Stop and remove existing container
+    if docker ps -a --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
+        print_status "Stopping existing container..."
+        docker stop "$CONTAINER_NAME" 2>/dev/null || true
+        print_status "Removing existing container..."
+        docker rm "$CONTAINER_NAME" 2>/dev/null || true
+    fi
+    
+    # Remove existing images
+    if docker images --format "table {{.Repository}}" | grep -q "^${IMAGE_NAME}$"; then
+        print_status "Removing existing images..."
+        docker rmi "$IMAGE_NAME:latest" 2>/dev/null || true
+    fi
+    
+    print_success "Cleanup completed."
+}
+
+# Function to load image
+load_image() {
+    local arch=$1
+    local tar_file="${DIST_DIR}/${IMAGE_NAME}-${arch}.tar.gz"
+    
+    print_status "Loading Docker image..."
+    
+    # Extract and load the image
+    gunzip -c "$tar_file" | docker load
+    
+    print_success "Image loaded successfully."
+}
+
+# Function to create data directories
+create_data_dirs() {
+    print_status "Creating data directories..."
+    
+    mkdir -p data/{data/apt-mirror,data/files,logs/apt-mirror,logs/nginx,conf/apt-mirror,conf/nginx/sites-available}
+    
+    # Set proper permissions
+    chmod 755 data/
+    chmod 755 data/*
+    
+    print_success "Data directories created."
+}
+
+# Function to generate apt-mirror configuration
+generate_mirror_config() {
+    local domain=$1
+    
+    print_status "Generating apt-mirror configuration..."
+    
+    cat > data/conf/apt-mirror/mirror.list << EOF
+# apt-mirror configuration for $domain
+# Generated on $(date)
+
+# Set base_path to the directory where you want to store the mirror
+set base_path    /var/spool/apt-mirror
+
+# Set mirror_path to the directory where you want to store the mirror
+set mirror_path  \$base_path/mirror
+
+# Set skel_path to the directory where you want to store the skeleton
+set skel_path    \$base_path/skel
+
+# Set var_path to the directory where you want to store the variable data
+set var_path     \$base_path/var
+
+# Set cleanscript to the script that cleans the mirror
+set cleanscript  \$var_path/clean.sh
+
+# Set defaultarch to the default architecture
+set defaultarch  amd64
+
+# Set postmirror_script to the script that runs after mirroring
+set postmirror_script \$var_path/postmirror.sh
+
+# Set run_postmirror to 1 to run the postmirror script
+set run_postmirror 0
+
+# Set nthreads to the number of threads to use
+set nthreads     20
+
+# Set _tilde to 1 to download tilde files
+set _tilde 0
+
+# Ubuntu 24.04 (Noble Numbat) repositories
+deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-security main restricted universe multiverse
+deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse
+
+# Debian 12 (Bookworm) repositories
+deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
+deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
+deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
+deb http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware
+
+# Clean up old packages
+clean http://archive.ubuntu.com/ubuntu
+clean http://deb.debian.org/debian
+clean http://security.debian.org/debian-security
+EOF
+    
+    print_success "apt-mirror configuration generated."
+}
+
+# Function to start container
+start_container() {
+    print_status "Starting container..."
+    
+    # Start with docker-compose
+    docker compose -f docker-compose.yml up -d
+    
+    print_success "Container started successfully."
+}
+
+# Function to show status
+show_status() {
+    print_status "Container status:"
+    docker ps --filter "name=$CONTAINER_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
+    
+    echo ""
+    print_status "Access URLs:"
+    if [ -f "docker-compose.yml" ]; then
+        # Extract values from generated docker-compose.yml
+        local mirror_domain=$(grep "MIRROR_DOMAIN" docker-compose.yml | sed 's/.*MIRROR_DOMAIN: //')
+        local admin_domain=$(grep "ADMIN_DOMAIN" docker-compose.yml | sed 's/.*ADMIN_DOMAIN: //')
+        local files_domain=$(grep "FILES_DOMAIN" docker-compose.yml | sed 's/.*FILES_DOMAIN: //')
+        local admin_pass=$(grep "ADMIN_PASSWORD" docker-compose.yml | sed 's/.*ADMIN_PASSWORD: //')
+        
+        echo "  Main Repository: http://$mirror_domain"
+        echo "  Admin Panel: http://$admin_domain (admin/$admin_pass)"
+        echo "  File Repository: http://$files_domain"
+    else
+        echo "  Main Repository: http://mirror.intra"
+        echo "  Admin Panel: http://admin.mirror.intra (admin/admin)"
+        echo "  File Repository: http://files.mirror.intra"
+    fi
+    
+    echo ""
+    print_status "Logs:"
+    echo "  docker logs $CONTAINER_NAME"
+    echo "  docker compose -f docker-compose.yml logs"
+}
+
+# Function to show usage
+show_usage() {
+    echo "Usage: $0 [OPTIONS]"
+    echo ""
+    echo "Options:"
+    echo "  --config-only     Only generate configuration, don't start container"
+    echo "  --no-cleanup      Skip cleanup of previous installation"
+    echo "  --help            Show this help message"
+    echo ""
+    echo "This script will:"
+    echo "  1. Detect your system architecture"
+    echo "  2. Validate that required image files exist"
+    echo "  3. Get your custom configuration"
+    echo "  4. Clean up previous installation"
+    echo "  5. Load the appropriate Docker image"
+    echo "  6. Start the container with your configuration"
+    echo ""
+    echo "Prerequisites:"
+    echo "  - Docker installed and running"
+    echo "  - Built images in dist/ directory (run ./build.sh first)"
+    echo "  - openssl for password hashing"
+}
+
+# Main execution
+main() {
+    local config_only=false
+    local no_cleanup=false
+    
+    # Parse command line arguments
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            --config-only)
+                config_only=true
+                shift
+                ;;
+            --no-cleanup)
+                no_cleanup=true
+                shift
+                ;;
+            --help)
+                show_usage
+                exit 0
+                ;;
+            *)
+                print_error "Unknown option: $1"
+                show_usage
+                exit 1
+                ;;
+        esac
+    done
+    
+    print_status "Starting ui-apt-mirror deployment..."
+    
+    # Detect architecture
+    local arch=$(detect_architecture)
+    print_success "Detected architecture: $arch"
+    
+    # Validate dist directory
+    validate_dist "$arch"
+    
+    # Get user configuration
+    get_user_config
+    
+    if [ "$config_only" = true ]; then
+        print_success "Configuration completed. Run without --config-only to start the container."
+        exit 0
+    fi
+    
+    # Clean up previous installation
+    if [ "$no_cleanup" = false ]; then
+        cleanup_previous
+    fi
+    
+    # Load image
+    load_image "$arch"
+    
+    # Create data directories
+    create_data_dirs
+    
+    # Generate apt-mirror configuration
+    if [ -f "$CONFIG_FILE" ]; then
+        source "$CONFIG_FILE"
+        generate_mirror_config "$MIRROR_DOMAIN"
+    else
+        generate_mirror_config "mirror.intra"
+    fi
+    
+    # Generate docker-compose.yml
+    if [ -f "$CONFIG_FILE" ]; then
+        source "$CONFIG_FILE"
+        generate_docker_compose "$MIRROR_DOMAIN" "$SYNC_FREQUENCY" "$ADMIN_PASSWORD"
+    else
+        generate_docker_compose "mirror.intra" "14400" "admin"
+    fi
+    
+    # Start container
+    start_container
+    
+    # Show status
+    show_status
+    
+    print_success "Deployment completed successfully!"
+}
+
+# Run main function with all arguments
+main "$@"