616 lines
18 KiB
Bash
Executable File
616 lines
18 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# apXtri Installation Script - New Structure
|
|
# Target directory structure:
|
|
# /opt/apxtowns/{TOWN}.{NATION}/apxtri/ (code)
|
|
# /var/lib/apxtowns/{TOWN}.{NATION}/{TOWN}/ (data - first/default tribe)
|
|
#
|
|
# Usage:
|
|
# sudo ./init.sh --town <name> --nation <name> [--clean] [--help]
|
|
#
|
|
# Example:
|
|
# sudo ./init.sh --town farm --nation test
|
|
|
|
set -eEuo pipefail
|
|
trap 'err_handler $LINENO $?' ERR
|
|
|
|
# ==== CONFIGURATION ====
|
|
URL_REPO="https://gitea.ndda.fr/apxtri"
|
|
REPO_APXTRI="$URL_REPO/apxtri.git"
|
|
APXTRI_USER="apxtri"
|
|
NODE_VERSION="lts"
|
|
|
|
# Default paths (can be overridden)
|
|
DEFAULT_DATAPATH_BASE="/var/lib/apxtowns"
|
|
DEFAULT_NODEPATH_BASE="/opt/apxtowns"
|
|
LOGPATH="/var/log/apxtowns"
|
|
|
|
# User-provided values
|
|
NATION=""
|
|
TOWN=""
|
|
MODE=""
|
|
APIPORT=""
|
|
DOMAIN=""
|
|
CLEAN_INSTALL=false
|
|
|
|
# ==== LOGGING ====
|
|
sudo touch /var/log/apxtri_install.log
|
|
sudo chown "$USER":"$USER" /var/log/apxtri_install.log 2>/dev/null || true
|
|
exec > >(tee -i /var/log/apxtri_install.log)
|
|
exec 2>&1
|
|
|
|
# ==== ERROR HANDLER ====
|
|
err_handler() {
|
|
local lineno="$1"
|
|
local code="$2"
|
|
if [[ "$code" -eq 99 ]]; then
|
|
echo "[INFO] Installation voluntarily stopped at line $lineno (exit 99)."
|
|
exit 99
|
|
fi
|
|
echo "[ERROR] Failure at line $lineno (code $code)."
|
|
echo "[INFO] Check logs above for details."
|
|
exit "$code"
|
|
}
|
|
|
|
# ==== USAGE ====
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $0 --town <name> --nation <name> [options]
|
|
|
|
Options:
|
|
--town <name> Town name (required)
|
|
--nation <name> Nation name (required, use 'test' for dev mode)
|
|
--domain <domain> Public domain (required for prod, auto in dev)
|
|
--apiport <port> API port (default: auto-detect from 3000)
|
|
--datapath <path> Base data path (default: /var/lib/apxtowns)
|
|
--nodepath <path> Base node path (default: /opt/apxtowns)
|
|
--clean Remove existing installation before install
|
|
--help Show this help message
|
|
|
|
Example:
|
|
sudo ./init.sh --town farm --nation test
|
|
sudo ./init.sh --town nova --nation libera --domain nova.libera.maildigit.fr
|
|
sudo ./init.sh --town mytown --nation ants --apiport 3001
|
|
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# ==== ARGUMENT PARSING ====
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--town)
|
|
TOWN="$2"
|
|
shift 2
|
|
;;
|
|
--nation)
|
|
NATION="$2"
|
|
shift 2
|
|
;;
|
|
--apiport)
|
|
APIPORT="$2"
|
|
shift 2
|
|
;;
|
|
--datapath)
|
|
DEFAULT_DATAPATH_BASE="$2"
|
|
shift 2
|
|
;;
|
|
--nodepath)
|
|
DEFAULT_NODEPATH_BASE="$2"
|
|
shift 2
|
|
;;
|
|
--domain)
|
|
DOMAIN="$2"
|
|
shift 2
|
|
;;
|
|
--clean)
|
|
CLEAN_INSTALL=true
|
|
shift
|
|
;;
|
|
--help|-h)
|
|
usage
|
|
;;
|
|
*)
|
|
echo "Error: Unknown option $1"
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ==== VALIDATION ====
|
|
if [[ -z "$TOWN" ]] || [[ -z "$NATION" ]]; then
|
|
echo "[ERROR] Both --town and --nation are required."
|
|
usage
|
|
fi
|
|
|
|
# Validate town/nation names (alphanumeric and hyphen only)
|
|
if ! [[ "$TOWN" =~ ^[a-z0-9]+$ ]]; then
|
|
echo "[ERROR] Town name must be lowercase alphanumeric only (a-z, 0-9)."
|
|
exit 1
|
|
fi
|
|
if ! [[ "$NATION" =~ ^[a-z0-9]+$ ]]; then
|
|
echo "[ERROR] Nation name must be lowercase alphanumeric only (a-z, 0-9)."
|
|
exit 1
|
|
fi
|
|
|
|
# ==== COMPUTED PATHS ====
|
|
NODEPATH="${DEFAULT_NODEPATH_BASE}/${TOWN}.${NATION}"
|
|
DATAPATH="${DEFAULT_DATAPATH_BASE}/${TOWN}.${NATION}"
|
|
PROJECT_DIR="${NODEPATH}/apxtri"
|
|
TRIBE_DATA_DIR="${DATAPATH}/${TOWN}"
|
|
TRIBE_CODE_DIR="${NODEPATH}/${TOWN}"
|
|
|
|
# ==== CLEANING ====
|
|
cleaning() {
|
|
echo "[CLEAN] Starting cleanup..."
|
|
|
|
# Kill processes
|
|
if [[ -n "${APIPORT}" ]]; then
|
|
sudo fuser -k "${APIPORT}/tcp" 2>/dev/null || true
|
|
fi
|
|
|
|
# Stop services
|
|
sudo systemctl stop "apxtri@${TOWN}.${NATION}" 2>/dev/null || true
|
|
sudo systemctl disable "apxtri@${TOWN}.${NATION}" 2>/dev/null || true
|
|
|
|
# Remove service
|
|
sudo rm -f "/etc/systemd/system/apxtri@.service"
|
|
sudo systemctl daemon-reload
|
|
|
|
# Remove directories
|
|
sudo rm -rf "$NODEPATH" "$DATAPATH"
|
|
|
|
# Clean hosts entries
|
|
sudo sed -i "/\.${TOWN}\.${NATION}/d" /etc/hosts 2>/dev/null || true
|
|
|
|
echo "[CLEAN] Cleanup complete."
|
|
}
|
|
|
|
if [[ "$CLEAN_INSTALL" == true ]]; then
|
|
echo "[WARN] Clean install requested. Removing existing installation..."
|
|
cleaning
|
|
fi
|
|
|
|
# ==== CHECK EXISTING INSTALLATION ====
|
|
if [[ -d "$NODEPATH" ]] || [[ -d "$DATAPATH" ]]; then
|
|
echo "[ERROR] Installation already exists at:"
|
|
echo " - $NODEPATH"
|
|
echo " - $DATAPATH"
|
|
echo ""
|
|
echo "To reinstall, run with --clean flag:"
|
|
echo " sudo $0 --town $TOWN --nation $NATION --clean"
|
|
exit 99
|
|
fi
|
|
|
|
# ==== MODE DETECTION ====
|
|
if [[ "$NATION" == "test" ]]; then
|
|
MODE="dev"
|
|
DOMAIN="admin.${TOWN}.${NATION}"
|
|
echo "[INFO] DEV mode (nation=test)"
|
|
else
|
|
MODE="prod"
|
|
if [[ -z "$DOMAIN" ]]; then
|
|
echo "[WARN] No domain provided. Use --domain <domain> for production."
|
|
echo "[WARN] You can set DOMAIN manually in .env later."
|
|
else
|
|
echo "[INFO] Using domain: $DOMAIN"
|
|
fi
|
|
echo "[INFO] PROD mode (nation=$NATION)"
|
|
fi
|
|
|
|
# ==== AUTO-DETECT FREE PORT ====
|
|
if [[ -z "$APIPORT" ]]; then
|
|
APIPORT=3000
|
|
while sudo lsof -Pi :$APIPORT -sTCP:LISTEN -t >/dev/null 2>&1; do
|
|
echo "[INFO] Port $APIPORT in use, trying next..."
|
|
APIPORT=$((APIPORT + 1))
|
|
done
|
|
fi
|
|
echo "[INFO] Using API port: $APIPORT"
|
|
|
|
# ==== CONFIRMATION ====
|
|
echo ""
|
|
echo "============================================"
|
|
echo "apXtri Installation Configuration"
|
|
echo "============================================"
|
|
echo " Mode: $MODE"
|
|
echo " Town: $TOWN"
|
|
echo " Nation: $NATION"
|
|
echo " Domain: $DOMAIN"
|
|
echo " API Port: $APIPORT"
|
|
echo " Data Path: $DATAPATH"
|
|
echo " Node Path: $NODEPATH"
|
|
echo " Project Dir: $PROJECT_DIR"
|
|
echo " Tribe Data: $TRIBE_DATA_DIR"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
read -p "Continue? [Y/n]: " confirm
|
|
if [[ "$confirm" =~ ^[Nn]$ ]]; then
|
|
echo "Installation cancelled."
|
|
exit 0
|
|
fi
|
|
|
|
# ==== STEP 1: SYSTEM DEPENDENCIES ====
|
|
echo "[STEP 1/11] Installing system dependencies..."
|
|
sudo apt update --fix-missing
|
|
sudo apt install -y curl git gnupg jq build-essential locales vim libcap2-bin p7zip-full unzip psmisc
|
|
|
|
# Ensure UTF-8 locale
|
|
if ! locale -a 2>/dev/null | grep -q '\.UTF-8'; then
|
|
echo "[INFO] Generating en_US.UTF-8 locale..."
|
|
sudo locale-gen en_US.UTF-8
|
|
sudo update-locale LANG=en_US.UTF-8
|
|
fi
|
|
|
|
# ==== STEP 2: CADDY ====
|
|
echo "[STEP 2/11] Installing Caddy..."
|
|
if ! command -v caddy &>/dev/null; then
|
|
sudo mkdir -p /usr/share/keyrings
|
|
sudo chmod 755 /usr/share/keyrings
|
|
|
|
sudo curl -fsSL https://dl.cloudsmith.io/public/caddy/stable/gpg.key -o /tmp/caddy-key.gpg
|
|
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg /tmp/caddy-key.gpg
|
|
sudo chmod 644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
|
sudo rm -f /tmp/caddy-key.gpg
|
|
|
|
echo "deb [signed-by=/usr/share/keyrings/caddy-stable-archive-keyring.gpg] https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main" | \
|
|
sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
|
|
|
sudo apt update && sudo apt install -y caddy
|
|
else
|
|
echo "[INFO] Caddy already installed."
|
|
fi
|
|
|
|
# ==== STEP 3: HOSTS CONFIGURATION ====
|
|
echo "[STEP 3/11] Configuring /etc/hosts..."
|
|
if [[ "$MODE" == "dev" ]]; then
|
|
if ! grep -q "$DOMAIN" /etc/hosts; then
|
|
echo "127.0.0.1 $DOMAIN" | sudo tee -a /etc/hosts
|
|
echo "[INFO] Added $DOMAIN to /etc/hosts"
|
|
fi
|
|
fi
|
|
|
|
# ==== STEP 4: CREATE USER ====
|
|
echo "[STEP 4/11] Creating system user..."
|
|
if ! id "$APXTRI_USER" &>/dev/null; then
|
|
sudo useradd --system --no-create-home --shell /usr/sbin/nologin "$APXTRI_USER"
|
|
echo "$APXTRI_USER:$APXTRI_USER" | sudo chpasswd
|
|
else
|
|
echo "[INFO] User $APXTRI_USER already exists."
|
|
fi
|
|
|
|
# ==== STEP 5: CREATE DIRECTORIES ====
|
|
echo "[STEP 5/11] Creating directories..."
|
|
sudo mkdir -p "$PROJECT_DIR"
|
|
sudo mkdir -p "$TRIBE_DATA_DIR"/{objects/{options,persons,wwws/{cdn/draft,cdn/optz,cdn/lib,townadmin}},logs,backup,tmp,blockchain}
|
|
sudo mkdir -p "$TRIBE_CODE_DIR"/{routes,models,schema,middlewares,wwws}
|
|
|
|
# Ownership
|
|
sudo chown -R "$APXTRI_USER:$APXTRI_USER" "$NODEPATH" "$DATAPATH"
|
|
sudo chmod -R g+rwX "$NODEPATH" "$DATAPATH"
|
|
|
|
echo "[INFO] Directory structure created:"
|
|
echo " $NODEPATH/"
|
|
echo " └── apxtri/ (core code)"
|
|
echo " └── $TOWN/ (tribe code)"
|
|
echo " $DATAPATH/"
|
|
echo " └── $TOWN/ (tribe data + blockchain)"
|
|
|
|
# ==== STEP 6: CLONE REPOSITORY ====
|
|
echo "[STEP 6/11] Cloning apxtri repository..."
|
|
if [[ -d "$PROJECT_DIR/.git" ]]; then
|
|
echo "[INFO] Removing existing repo for clean clone..."
|
|
sudo rm -rf "$PROJECT_DIR"
|
|
fi
|
|
sudo -u "$APXTRI_USER" HOME="$PROJECT_DIR" git clone "$REPO_APXTRI" "$PROJECT_DIR"
|
|
sudo -u "$APXTRI_USER" HOME="$PROJECT_DIR" git config --global --add safe.directory "$PROJECT_DIR"
|
|
|
|
# ==== STEP 7: COPY SKELETON ====
|
|
echo "[STEP 7/11] Setting up tribe skeleton..."
|
|
SKELETON_DATA="${PROJECT_DIR}/setup/skeletons/newtribe/data"
|
|
SKELETON_CODE="${PROJECT_DIR}/setup/skeletons/newtribe/code"
|
|
|
|
# Create essential object directories
|
|
for obj in nations towns tribes; do
|
|
sudo mkdir -p "$TRIBE_DATA_DIR/objects/$obj/itm"
|
|
done
|
|
|
|
# Copy code skeleton (if exists)
|
|
if [[ -d "$SKELETON_CODE" ]]; then
|
|
sudo cp -r "$SKELETON_CODE"/* "$TRIBE_CODE_DIR/" 2>/dev/null || true
|
|
echo "[INFO] Code skeleton copied."
|
|
fi
|
|
|
|
# Copy data skeleton (townadmin, etc.) to tribe data dir
|
|
if [[ -d "$SKELETON_DATA" ]]; then
|
|
sudo cp -r "$SKELETON_DATA"/* "$TRIBE_DATA_DIR/" 2>/dev/null || true
|
|
echo "[INFO] Data skeleton copied."
|
|
fi
|
|
|
|
# Create missing directories for apidoc
|
|
sudo mkdir -p "$TRIBE_CODE_DIR/routes"
|
|
sudo mkdir -p "$TRIBE_DATA_DIR/wwws/apidoc/dist"
|
|
sudo mkdir -p "$TRIBE_DATA_DIR/wwws/cdn/lib"
|
|
sudo mkdir -p "$TRIBE_DATA_DIR/wwws/cdn/draft"
|
|
sudo mkdir -p "$TRIBE_DATA_DIR/wwws/cdn/optz"
|
|
|
|
# Render Mustache templates in skeleton files
|
|
if [[ -f "$TRIBE_CODE_DIR/wwws/apidoc/conf.json" ]]; then
|
|
sed -i \
|
|
-e "s/{{TOWN}}/$TOWN/g" \
|
|
-e "s/{{NATION}}/$NATION/g" \
|
|
-e "s/{{tribe}}/$TOWN/g" \
|
|
-e "s|{{NODEPATH}}|$NODEPATH|g" \
|
|
-e "s|{{DATAPATH}}|$DATAPATH|g" \
|
|
"$TRIBE_CODE_DIR/wwws/apidoc/conf.json"
|
|
echo "[INFO] apidoc conf.json rendered."
|
|
fi
|
|
|
|
# Render Mustache templates in code townadmin appconf.json
|
|
if [[ -f "$TRIBE_CODE_DIR/wwws/townadmin/appconf.json" ]]; then
|
|
sed -i \
|
|
-e "s/{{TOWN}}/$TOWN/g" \
|
|
-e "s/{{NATION}}/$NATION/g" \
|
|
-e "s/{{tribe}}/$TOWN/g" \
|
|
-e "s|{{NODEPATH}}|$NODEPATH|g" \
|
|
-e "s|{{DATAPATH}}|$DATAPATH|g" \
|
|
"$TRIBE_CODE_DIR/wwws/townadmin/appconf.json"
|
|
echo "[INFO] townadmin appconf.json rendered."
|
|
fi
|
|
|
|
# Render variables {TOWN}, {NATION}, {tribe} in data skeleton files
|
|
if [[ -f "$TRIBE_DATA_DIR/wwws/townadmin/appconf.json" ]]; then
|
|
sudo sed -i \
|
|
-e "s/{TOWN}/$TOWN/g" \
|
|
-e "s/{NATION}/$NATION/g" \
|
|
-e "s/{tribe}/$TOWN/g" \
|
|
"$TRIBE_DATA_DIR/wwws/townadmin/appconf.json"
|
|
echo "[INFO] townadmin appconf.json rendered."
|
|
fi
|
|
if [[ -f "$TRIBE_DATA_DIR/wwws/townadmin/index.html" ]]; then
|
|
sudo sed -i \
|
|
-e "s/{TOWN}/$TOWN/g" \
|
|
-e "s/{NATION}/$NATION/g" \
|
|
-e "s/{tribe}/$TOWN/g" \
|
|
"$TRIBE_DATA_DIR/wwws/townadmin/index.html"
|
|
echo "[INFO] townadmin index.html rendered."
|
|
fi
|
|
|
|
# Create conf.json and initial items for essential objects
|
|
for obj in nations towns tribes; do
|
|
CONF_FILE="$TRIBE_DATA_DIR/objects/$obj/conf.json"
|
|
if [[ ! -f "$CONF_FILE" ]]; then
|
|
echo "{\"name\":\"$obj\",\"schema\":\"apxtri/schema/$obj.json\",\"lastupdate\":0}" | \
|
|
sudo tee "$CONF_FILE" > /dev/null
|
|
fi
|
|
done
|
|
|
|
# Create initial nation item
|
|
NATION_ITEM="$TRIBE_DATA_DIR/objects/nations/itm/${NATION}.json"
|
|
if [[ ! -f "$NATION_ITEM" ]]; then
|
|
echo "{\"nationId\":\"$NATION\",\"dtcreate\":\"$(date -Iseconds)\"}" | \
|
|
sudo tee "$NATION_ITEM" > /dev/null
|
|
echo "[INFO] Nation '$NATION' created."
|
|
fi
|
|
|
|
# Create initial town item
|
|
TOWN_ITEM="$TRIBE_DATA_DIR/objects/towns/itm/${TOWN}.json"
|
|
if [[ ! -f "$TOWN_ITEM" ]]; then
|
|
echo "{\"townId\":\"$TOWN\",\"nationId\":\"$NATION\",\"status\":\"unchain\",\"dns\":[\"admin.${TOWN}.${NATION}\"]}" | \
|
|
sudo tee "$TOWN_ITEM" > /dev/null
|
|
echo "[INFO] Town '$TOWN' created."
|
|
fi
|
|
|
|
# Create initial tribe item (same name as TOWN)
|
|
TRIBE_ITEM="$TRIBE_DATA_DIR/objects/tribes/itm/${TOWN}.json"
|
|
if [[ ! -f "$TRIBE_ITEM" ]]; then
|
|
cat <<TRIBE_EOF | sudo tee "$TRIBE_ITEM" > /dev/null
|
|
{
|
|
"tribeId": "$TOWN",
|
|
"api": {
|
|
"languages": ["en", "fr"],
|
|
"appset": { "trust proxy": true },
|
|
"json": { "limit": "10mb", "type": "application/json", "rawBody": true },
|
|
"bodyparse": {
|
|
"urlencoded": { "limit": "50mb", "extended": true },
|
|
"json": { "limit": "500mb" }
|
|
}
|
|
},
|
|
"emailcontact": "",
|
|
"smtp": {},
|
|
"sms": {}
|
|
}
|
|
TRIBE_EOF
|
|
echo "[INFO] Tribe '$TOWN' created."
|
|
fi
|
|
|
|
# Fix ownership after sudo cp/mkdir in step 7 (files created as root)
|
|
sudo chown -R "$APXTRI_USER:$APXTRI_USER" "$TRIBE_DATA_DIR" "$TRIBE_CODE_DIR"
|
|
|
|
# ==== STEP 8: CREATE .ENV FILE ====
|
|
echo "[STEP 8/11] Creating environment configuration..."
|
|
|
|
# Get IP addresses
|
|
LAN_IP=$(hostname -I | awk '{print $1}')
|
|
PUBLIC_IP=$(curl -s --connect-timeout 5 http://icanhazip.com || echo "$LAN_IP")
|
|
echo "[INFO] LAN IP: $LAN_IP"
|
|
echo "[INFO] Public IP: $PUBLIC_IP"
|
|
|
|
sudo tee "${PROJECT_DIR}/.env" > /dev/null <<EOL
|
|
URL_REPO="$URL_REPO"
|
|
VERSION="1.0.0"
|
|
MODE="$MODE"
|
|
TOWN="$TOWN"
|
|
NATION="$NATION"
|
|
DOMAIN="$DOMAIN"
|
|
UBUNTU="server"
|
|
ETCCONF="/etc/apxtowns/apxtowns"
|
|
DATAPATH="$DATAPATH"
|
|
NODEPATH="$NODEPATH"
|
|
LOGPATH="$LOGPATH"
|
|
APIPORT="$APIPORT"
|
|
ACTIVELOG=apxtri,isAuthenticated,checkHeaders,Odmdb,Pagans,Persons,Wwws,Trackings,Notifications
|
|
LOG_LEVEL=debug
|
|
EXPOSEDHEADERS="xdays,xalias,xlang,xtribe,xapp,xuuid"
|
|
SOCKETPORT="$((APIPORT + 1))"
|
|
BACKENDURL="localhost"
|
|
CADDYAPIURL="http://localhost:2019"
|
|
BLOCKCHAIN_CONNECT=false
|
|
PRIVKMAYOR=""
|
|
LOCALIP="$LAN_IP"
|
|
PUBLICIP="$PUBLIC_IP"
|
|
EOL
|
|
|
|
sudo chown "$APXTRI_USER:$APXTRI_USER" "${PROJECT_DIR}/.env"
|
|
echo "[INFO] .env file created."
|
|
|
|
# ==== STEP 9: NODE.JS & NPM ====
|
|
echo "[STEP 9/11] Installing Node.js..."
|
|
NVM_DIR="${PROJECT_DIR}/.nvm"
|
|
npm_config_cache="${DATAPATH}/.npm"
|
|
sudo mkdir -p "$npm_config_cache"
|
|
sudo chown -R "$APXTRI_USER:$APXTRI_USER" "$npm_config_cache"
|
|
|
|
sudo -u "$APXTRI_USER" HOME="$PROJECT_DIR" \
|
|
NVM_DIR="$NVM_DIR" \
|
|
PROJECT_DIR="$PROJECT_DIR" \
|
|
npm_config_cache="$npm_config_cache" \
|
|
bash <<'NVMEOF'
|
|
set -euo pipefail
|
|
if [ ! -d "$NVM_DIR" ]; then
|
|
git clone https://github.com/nvm-sh/nvm.git "$NVM_DIR"
|
|
cd "$NVM_DIR"
|
|
git checkout master
|
|
fi
|
|
chmod +x "$NVM_DIR/nvm.sh"
|
|
export NVM_DIR="$NVM_DIR"
|
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
nvm install --lts
|
|
nvm alias default 'lts/*'
|
|
nvm use default
|
|
npm install -g npm@latest
|
|
|
|
cd "$PROJECT_DIR"
|
|
npm install
|
|
NVMEOF
|
|
|
|
echo "[INFO] Node.js installed."
|
|
echo "[INFO] npm dependencies installed."
|
|
|
|
# ==== STEP 10: SYSTEMD SERVICE ====
|
|
echo "[STEP 10/11] Creating systemd service..."
|
|
|
|
# Create template service
|
|
sudo tee "/etc/systemd/system/apxtri@.service" > /dev/null <<EOF
|
|
[Unit]
|
|
Description=apXtri node %i
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$APXTRI_USER
|
|
Group=$APXTRI_USER
|
|
WorkingDirectory=$PROJECT_DIR
|
|
EnvironmentFile=$PROJECT_DIR/.env
|
|
ExecStart=/bin/bash -lc "source $NVM_DIR/nvm.sh && node apxtri.js"
|
|
ExecStop=/bin/bash -lc "pkill -u $APXTRI_USER -f 'node.*apxtri.js'"
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable "apxtri@${TOWN}.${NATION}"
|
|
sudo systemctl start "apxtri@${TOWN}.${NATION}"
|
|
|
|
# ==== HELPER SCRIPTS (usr/local/bin) ====
|
|
echo "[STEP 12] Configuring automatic updates (cron)..."
|
|
|
|
CRON_FILE="/etc/cron.d/apxtri-${TOWN}.${NATION}"
|
|
UPDATE_LOG="${TRIBE_DATA_DIR}/logs/update.log"
|
|
CRON_TEMP=$(mktemp)
|
|
|
|
mkdir -p "$(dirname "$UPDATE_LOG")"
|
|
|
|
cat > "$CRON_TEMP" << CRONEOF
|
|
# apxtri ${TOWN}.${NATION} - automatic updates
|
|
SHELL=/bin/bash
|
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
|
|
# Update check daily at 3:00 AM
|
|
0 3 * * * root /opt/apxtowns/${TOWN}.${NATION}/apxtri/setup/update.sh >> ${UPDATE_LOG} 2>&1
|
|
CRONEOF
|
|
|
|
if [[ -f "$CRON_FILE" ]]; then
|
|
echo "[INFO] Cron file already exists: $CRON_FILE"
|
|
else
|
|
sudo cp "$CRON_TEMP" "$CRON_FILE" && echo "[INFO] Cron file created: $CRON_FILE"
|
|
fi
|
|
|
|
rm -f "$CRON_TEMP"
|
|
|
|
echo "[STEP 13] Configuring automatic backups (cron)..."
|
|
|
|
BACKUP_LOG="${TRIBE_DATA_DIR}/logs/backup.log"
|
|
mkdir -p "$(dirname "$BACKUP_LOG")"
|
|
|
|
cat >> "$CRON_FILE" << CRONEOF
|
|
|
|
# Backup: daily at 4:00 AM (dayname = day of week)
|
|
0 4 * * * root /opt/apxtowns/${TOWN}.${NATION}/apxtri/setup/backup.sh dayname >> ${BACKUP_LOG} 2>&1
|
|
CRONEOF
|
|
|
|
echo "[INFO] Backup cron entry added to $CRON_FILE"
|
|
|
|
echo "[STEP 11] Installing helper scripts to /usr/local/bin..."
|
|
USRLOCALBIN_DIR="${PROJECT_DIR}/setup/usrlocalbin"
|
|
if [[ -d "$USRLOCALBIN_DIR" ]]; then
|
|
for script in "$USRLOCALBIN_DIR"/*; do
|
|
if [[ -f "$script" && -x "$script" ]]; then
|
|
script_name=$(basename "$script")
|
|
sudo cp "$script" "/usr/local/bin/$script_name"
|
|
sudo chmod +x "/usr/local/bin/$script_name"
|
|
echo "[INFO] Installed: $script_name"
|
|
fi
|
|
done
|
|
else
|
|
echo "[INFO] No scripts in $USRLOCALBIN_DIR"
|
|
fi
|
|
|
|
# ==== FINAL MESSAGE ====
|
|
echo ""
|
|
echo "============================================"
|
|
echo "INSTALLATION COMPLETE"
|
|
echo "============================================"
|
|
echo ""
|
|
echo "Configuration:"
|
|
echo " Instance: $TOWN.$NATION"
|
|
echo " Mode: $MODE"
|
|
echo " API Port: $APIPORT"
|
|
echo ""
|
|
echo "Directories:"
|
|
echo " Code: $PROJECT_DIR"
|
|
echo " Data: $DATAPATH"
|
|
echo " Tribe Data: $TRIBE_DATA_DIR"
|
|
echo " Tribe Code: $TRIBE_CODE_DIR"
|
|
echo ""
|
|
echo "Service:"
|
|
echo " Start: sudo systemctl start apxtri@$TOWN.$NATION"
|
|
echo " Stop: sudo systemctl stop apxtri@$TOWN.$NATION"
|
|
echo " Status: sudo systemctl status apxtri@$TOWN.$NATION"
|
|
echo " Logs: apxdev $TOWN.$NATION logs"
|
|
echo ""
|
|
echo "Helper:"
|
|
echo " apxdev - Manage apxtri instances"
|
|
echo ""
|
|
if [[ "$MODE" == "dev" ]]; then
|
|
echo "Access: http://$DOMAIN:$APIPORT"
|
|
fi
|
|
echo ""
|
|
echo "============================================"
|