#!/bin/bash APP_DIR="$HOME/.db-middleware" CODE_DIR="$APP_DIR/code" CONFIG_DIR="$APP_DIR/configs" CONFIG_FILE="$CONFIG_DIR/app.conf" LOADED_CONFIG=0 IMAGE_NAME="db-middleware" CONTAINER_NAME="con-db-middleware" REPO_URL="https://gitea.abdulhade.com/abdulhade/db-middleware.git" EXECUTION_MESSAGE="NO-RETURN" print_header() { local HEADER_TEXT="$1" local MAX_LENGTH=0 # Split the header text into lines and find the maximum line length while IFS= read -r LINE; do local LINE_LENGTH=${#LINE} if (( LINE_LENGTH > MAX_LENGTH )); then MAX_LENGTH=$LINE_LENGTH fi done <<< "$HEADER_TEXT" local BORDER_LENGTH=$((MAX_LENGTH + 2)) # Add 4 for padding (2 spaces) # Print the top border printf '+%*s+\n' "$BORDER_LENGTH" "" | tr ' ' '-' # Print each line of the header text while IFS= read -r LINE; do printf "| %-*s |\n" "$MAX_LENGTH" "$LINE" done <<< "$HEADER_TEXT" # Print the bottom border printf '+%*s+\n' "$BORDER_LENGTH" "" | tr ' ' '-' } build_docker_image() { echo "Building Docker image..." if docker build -t "$IMAGE_NAME" .; then echo "Docker image built successfully." return 0 else echo "Failed to build Docker image." return 1 fi } test() { echo "I am here" return 1 } exec_in_container() { # Returns 0 if the command was successful local CONTAINER_NAME=$CONTAINER_NAME local COMMAND="$1" # Check if the container is running if ! docker ps --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then echo "Error: Container '$CONTAINER_NAME' is not running." return 1 fi # Execute the command in the container OUTPUT=$(docker exec "$CONTAINER_NAME" bash -c "$COMMAND" 2>&1) local EXIT_CODE=$? # Check if the command succeeded if [[ $EXIT_CODE -eq 0 ]]; then EXECUTION_MESSAGE="$OUTPUT" # Return the output return 0 else echo "Error: Command failed in container '$CONTAINER_NAME' with exit code $EXIT_CODE." echo "Output: $OUTPUT" return $EXIT_CODE fi } set_up_scripts() { echo "Setting up scripts..." # Copy scripts if ! cp "$CODE_DIR/scripts/"* "$APP_DIR/scripts/"; then echo "Failed to copy scripts." return 1 fi # Give execution permission if ! chmod +x "$APP_DIR/scripts/"*; then echo "Failed to set execution permissions." return 1 fi # Create ~/.local/bin if it doesn't exist mkdir -p "$HOME/.local/bin" # Create symlink if ! ln -sf "$APP_DIR/scripts/manager.sh" "$HOME/.local/bin/db-middleware"; then echo "Failed to create symlink." # TODO Here we should handle the case of having the symlink created previously. return 1 fi # Update PATH if ! grep -q "$HOME/.local/bin" ~/.bashrc; then echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc fi # Reload bashrc if ! source ~/.bashrc; then echo "Failed to reload ~/.bashrc." return 1 fi echo "Scripts set up successfully." return 0 } load_config() { if [[ -f "$CONFIG_FILE" ]]; then if source "$CONFIG_FILE"; then LOADED_CONFIG=1 echo "Config file loaded successfully." return 0 else echo "Failed to load config file." return 1 fi else echo "Config file not found: $CONFIG_FILE" return 1 fi } show_config() { if [[ $LOADED_CONFIG -eq 0 ]]; then echo "Config not loaded. Please load the config file first." return 1 fi echo "Current Config:" echo "CONTAINER_NAME: ${CONTAINER_NAME:-Not set}" echo "API_PORT: ${API_PORT:-Not set}" echo "HAS_LOCAL_DBS: ${HAS_LOCAL_DBS:-Not set}" local RUN_COMMAND="docker run --name $CONTAINER_NAME" if [[ $HAS_LOCAL_DBS -eq 1 ]]; then RUN_COMMAND+=" --network host" fi RUN_COMMAND+=" -p ${API_PORT:-8080}:8080 $IMAGE_NAME" echo "Run Command: $RUN_COMMAND" return 0 } convert_ports_to_docker_args() { local ports="$1" local docker_args="" if [[ -z "$ports" ]]; then echo "No ports provided." return 1 fi # Split the ports by comma and trim whitespace IFS=',' read -r -a port_array <<< "$ports" # Loop through the ports and format them as Docker arguments for port in "${port_array[@]}"; do port=$(echo "$port" | xargs) # Trim whitespace if [[ ! "$port" =~ ^[0-9]+$ ]]; then echo "Invalid port: $port" return 1 fi docker_args+=" -p $port:$port" done echo "$docker_args" return 0 } install() { echo echo "+------------------------------+" echo "| Installing the Middleware... |" echo "+------------------------------+" echo if ! cd "$CODE_DIR"; then echo "Failed to navigate to $CODE_DIR." return 1 fi if ! build_docker_image; then echo "Failed to build Docker image." return 1 fi echo echo "+----------------------------------------+" echo "| Installed the Middleware Successfully! |" echo "+----------------------------------------+" echo echo "- You can run the middleware simply using the manager:" echo " >>> db-middleware start" echo "- Or directly by running the docker container:" echo " >>> docker run $IMAGE_NAME -p : -v /path/to/app/directory/" return 0 } pull_clone_repo() { local UPDATED=-1 # Default: No changes echo "Setting up repository..." # Create directories if they don't exist mkdir -p "$APP_DIR/{code,configs,scripts}" # Navigate to the code directory if ! cd "$CODE_DIR"; then echo "Failed to navigate to $CODE_DIR." return 1 # Error fi # Check if the directory is empty if [[ -z "$(ls -A $CODE_DIR)" ]]; then echo "Directory is empty. Cloning repository..." if ! git clone "$REPO_URL" .; then echo "Failed to clone repository." return 1 # Error fi echo "Repository cloned successfully." UPDATED=0 # Changes were applied else # Check if the directory contains a Git repository if [[ -d ".git" ]]; then echo "Directory contains a Git repository." # Fetch the latest changes if ! git fetch origin; then echo "Failed to fetch changes from remote." return 1 # Error fi # Get the local and remote HEAD commit hashes LOCAL_HEAD=$(git rev-parse HEAD) REMOTE_HEAD=$(git rev-parse origin/main) exec_in_container "git rev-parse HEAD" local RETURN_CODE=$? CONTAINER_HEAD=$EXECUTION_MESSAGE EXECUTION_MESSAGE="NO-RETURN" echo echo "- Commit hash in remote origin: $REMOTE_HEAD" echo "- Commit hash in local repo: $LOCAL_HEAD" if [[ $RETURN_CODE -eq 0 ]]; then echo "- Commit hash in container: $CONTAINER_HEAD" else echo "Failed to get commit hash from container." fi echo if [[ "$CONTAINER_HEAD" == "$LOCAL_HEAD" && "$LOCAL_HEAD" == "$REMOTE_HEAD" ]]; then echo "Repo is up to date." UPDATED=-1 elif [[ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]]; then echo "Remote repository has new changes. Pulling latest changes..." if ! git pull "$REPO_URL"; then echo "Failed to pull changes." return 1 # Error fi echo "Local Repository updated successfully." LOCAL_HEAD=$(git rev-parse HEAD) UPDATED=0 # Changes were applied else echo "Local Repository is already up to date." UPDATED=-1 # No changes # echo $UPDATED fi if [[ "$LOCAL_HEAD" != "$CONTAINER_HEAD" ]]; then echo "Container Repository is not up to date." fi else echo "Directory is not empty and does not contain a Git repository." echo "Please ensure the directory is empty or contains a valid Git repository." return 1 # Error fi fi echo "Repository setup completed successfully." return $UPDATED # -1 = No changes, 0 = Changes applied } update_code() { print_header "Checking for updates..." echo pull_clone_repo local UPDATED=$? echo case $UPDATED in # 255 is -1, because Bash return codes are unsigned 8-bit integer, limited to the range 0 to 255. -1|255) print_header "No changes detected." return -1 # No changes ;; -0) print_header "Changes were detected and applied to local repo. Need to rebuild the container, run: >>> db-middleware upgrade " return 0 # Changes applied ;; 1) print_header "Failed to update or clone repository." return 1 # Error ;; *) print_header "Wrong return code: \`$UPDATED\` from pull_clone_repo." return 1 ;; esac } upgrade() { echo echo "+-----------------------------+" echo "| Upgrading the Middleware... |" echo "+-----------------------------+" echo update_code local UPDATE_RESULT=$? case $UPDATE_RESULT in # 255 is -1, because Bash return codes are unsigned 8-bit integer, limited to the range 0 to 255. -1|255) echo echo "+------------------------------------------+" echo "| No changes detected. Skipping upgrade... |" echo "+------------------------------------------+" echo ;; 0) echo echo "+--------------------------------+" echo "| Rebuilding the Docker Image... |" echo "+--------------------------------+" echo if ! build_docker_image; then echo "Failed to rebuild Docker image." return 1 # Error fi if ! set_up_scripts; then echo "Failed to set up scripts." return 1 # Error fi echo echo "+---------------------------------------+" echo "| Upgraded the Middleware Successfully! |" echo "+---------------------------------------+" echo ;; 1) echo "Failed to update code. Upgrade aborted." return 1 # Error ;; esac return 0 } status() { echo "Checking container status..." if docker ps --filter "name=$CONTAINER_NAME" --format "{{.Status}}"; then return 0 else echo "Failed to check container status." return 1 fi } start() { echo "+---------------------------+" echo "| Starting the Container... |" echo "+---------------------------+" echo if ! docker run --name "$CONTAINER_NAME" -p "${API_PORT:-8080}:8080" "$IMAGE_NAME"; then echo "Failed to start container." return 1 fi return 0 } stop() { echo "Stopping the container..." if docker stop "$CONTAINER_NAME"; then echo "Container stopped successfully." return 0 else echo "Failed to stop container." return 1 fi } usage() { print_header "Usage: >>> $0 {install|upgrade|update_code|status|start|stop|show_config}" exit 1 } main() { # Check if an argument is provided if [[ $# -eq 0 ]]; then usage fi if ! load_config; then echo "Failed to load config. Exiting." exit 1 fi # Handle the argument case "$1" in install) install ;; update_code) update_code ;; upgrade) upgrade ;; status) status ;; start) start ;; show_config) show_config ;; stop) stop ;; *) echo "Invalid argument: $1"; usage ;; esac } # Run the script with the provided arguments main "$@"