#!/usr/bin/env bash # Runs inside the MyOffice LXC: PostgreSQL + ASP.NET Core runtime + app from Gitea Release. # No Docker, no nginx, no git, no SDK, no Node. # # Env (from proxmox/myoffice.sh): # APP_DIR, PUBLIC_URL, API_PORT, ASSETS_DIR # GITEA_URL, GITEA_OWNER, GITEA_REPO, RELEASE_TAG, RELEASE_ASSET_URL, GITEA_TOKEN set -euo pipefail APP_DIR="${APP_DIR:-/opt/myoffice}" API_DIR="${APP_DIR}/api" ASSETS_DIR="${ASSETS_DIR:-/tmp/myoffice-proxmox}" PUBLIC_URL="${PUBLIC_URL:-}" API_PORT="${API_PORT:-9100}" PG_DB="myoffice" PG_USER="myoffice" PG_PASS="myoffice" SKIP_FETCH="${SKIP_FETCH:-0}" export DEBIAN_FRONTEND=noninteractive export API_DIR # Avoid perl locale warnings in minimal LXC (host may export unsupported LC_*). export LANG=C.UTF-8 export LC_ALL=C.UTF-8 unset LANGUAGE LC_TIME LC_MESSAGES 2>/dev/null || true msg() { echo -e " ==> $*"; } # Optional: map Gitea hostname → LAN IP (GITEA_HOSTS_IP); skip if unset. ensure_gitea_hosts() { if [[ -z "${GITEA_HOSTS_IP:-}" ]]; then return 0 fi local hn="${GITEA_HOSTS_NAME:-mygit.ase.com.ua}" sed -i "/[[:space:]]${hn}\$/d" /etc/hosts echo "${GITEA_HOSTS_IP} ${hn}" >> /etc/hosts msg "hosts: ${GITEA_HOSTS_IP} ${hn}" } as_postgres() { # Script runs as root in CT; sudo is not installed. # cd away from /root — postgres cannot access it (harmless but noisy otherwise). if command -v runuser >/dev/null 2>&1; then (cd /tmp && runuser -u postgres -- "$@") else (cd /tmp && su -s /bin/bash postgres -c "$(printf '%q ' "$@")") fi } detect_public_url() { if [[ -n "$PUBLIC_URL" ]]; then PUBLIC_URL=$(echo "$PUBLIC_URL" | sed 's:/*$::') return 0 fi local ip ip=$(ip -4 -o addr show eth0 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1 || true) if [[ -z "$ip" ]]; then ip=$(hostname -I 2>/dev/null | awk '{print $1}') fi if [[ -z "$ip" ]]; then echo "Could not detect CT IP. Set PUBLIC_URL." >&2 exit 1 fi PUBLIC_URL="http://${ip}:${API_PORT}" } install_base() { msg "Installing base packages" apt-get update -y apt-get install -y ca-certificates curl gnupg locales postgresql postgresql-contrib sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen 2>/dev/null || true locale-gen en_US.UTF-8 >/dev/null 2>&1 || true update-locale LANG=en_US.UTF-8 >/dev/null 2>&1 || true } install_aspnet_runtime() { if command -v dotnet >/dev/null 2>&1 && dotnet --list-runtimes 2>/dev/null | grep -q 'Microsoft.AspNetCore.App 10\.'; then msg "ASP.NET Core 10 runtime already installed" return 0 fi msg "Installing ASP.NET Core 10 runtime" curl -fsSL https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -o /tmp/packages-microsoft-prod.deb dpkg -i /tmp/packages-microsoft-prod.deb apt-get update -y apt-get install -y aspnetcore-runtime-10.0 } setup_postgres() { msg "Configuring PostgreSQL (UTF8 — required for Npgsql ICU collations)" systemctl enable --now postgresql # Ensure locale exists before CREATE DATABASE … LC_COLLATE sed -i 's/^# *en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen 2>/dev/null || true locale-gen en_US.UTF-8 >/dev/null 2>&1 || true if ! as_postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${PG_USER}'" | grep -q 1; then as_postgres psql -c "CREATE USER ${PG_USER} WITH PASSWORD '${PG_PASS}';" else as_postgres psql -c "ALTER USER ${PG_USER} WITH PASSWORD '${PG_PASS}';" fi local enc enc=$(as_postgres psql -tAc "SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname='${PG_DB}'" 2>/dev/null | tr -d '[:space:]' || true) if [[ -n "$enc" && "$enc" != "UTF8" ]]; then msg "Recreating ${PG_DB}: encoding was ${enc}, need UTF8" as_postgres psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='${PG_DB}' AND pid <> pg_backend_pid();" || true as_postgres psql -c "DROP DATABASE IF EXISTS ${PG_DB};" enc="" fi if [[ -z "$enc" ]]; then as_postgres psql -c "CREATE DATABASE ${PG_DB} OWNER ${PG_USER} ENCODING 'UTF8' LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;" \ || as_postgres psql -c "CREATE DATABASE ${PG_DB} OWNER ${PG_USER} ENCODING 'UTF8' LC_COLLATE 'C.UTF-8' LC_CTYPE 'C.UTF-8' TEMPLATE template0;" fi as_postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${PG_DB} TO ${PG_USER};" as_postgres psql -d "$PG_DB" -c "GRANT ALL ON SCHEMA public TO ${PG_USER};" || true enc=$(as_postgres psql -tAc "SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname='${PG_DB}'" | tr -d '[:space:]') msg "PostgreSQL ready (db/user/pass=${PG_DB}, encoding=${enc})" } write_runtime_config() { mkdir -p "$API_DIR" if [[ ! -f "$API_DIR/appsettings.Production.json" ]]; then cat >"$API_DIR/appsettings.Production.json" <"$API_DIR/appsettings.shared.Production.json" <&2 exit 1 fi # shellcheck disable=SC1090 source "$fetch_script" download_and_extract } install_systemd() { msg "Configuring systemd unit myoffice-api" local unit_src="${ASSETS_DIR}/myoffice-api.service" if [[ ! -f "$unit_src" ]]; then unit_src="${APP_DIR}/proxmox/myoffice-api.service" fi if [[ ! -f "$unit_src" ]]; then echo "Missing myoffice-api.service" >&2 exit 1 fi sed -e "s|__API_DIR__|${API_DIR}|g" -e "s|__API_PORT__|${API_PORT}|g" \ "$unit_src" >/etc/systemd/system/myoffice-api.service mkdir -p "${APP_DIR}/proxmox" if [[ -d "$ASSETS_DIR" ]]; then cp -a "${ASSETS_DIR}/." "${APP_DIR}/proxmox/" fi chmod +x "${APP_DIR}/proxmox/"*.sh 2>/dev/null || true cat >"${APP_DIR}/proxmox/env" </dev/null || true msg "App DLL missing — service not started" fi } print_done() { echo echo " MyOffice CT is ready:" echo " Public URL ${PUBLIC_URL}" echo " Listen 0.0.0.0:${API_PORT} (proxy this from your nginx LXC)" echo " Postgres 127.0.0.1:5432 db/user/pass=${PG_DB}" echo " App dir ${API_DIR}" echo echo " Update later: ${APP_DIR}/proxmox/update.sh" echo " Demo login: user_UAH@user_UAH.userUAH / user_UAH" echo } detect_public_url ensure_gitea_hosts install_base install_aspnet_runtime setup_postgres write_runtime_config fetch_app install_systemd print_done