#!/bin/bash

set -eu -o pipefail

# This script is run after the box code is switched. This means that this script
# should pretty much always succeed. No network logic/download code here.

function log() {
  echo -e "$(date +'%Y-%m-%dT%H:%M:%S')" "==> start: $1"
}

log "Cloudron Start"

readonly USER="yellowtent"
readonly HOME_DIR="/home/${USER}"
readonly BOX_SRC_DIR="${HOME_DIR}/box"
readonly PLATFORM_DATA_DIR="${HOME_DIR}/platformdata"
readonly APPS_DATA_DIR="${HOME_DIR}/appsdata"
readonly BOX_DATA_DIR="${HOME_DIR}/boxdata/box"
readonly MAIL_DATA_DIR="${HOME_DIR}/boxdata/mail"

readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ubuntu_version=$(lsb_release -rs)

vergte() {
    greater_version=$(echo -e "$1\n$2" | sort -rV | head -n1)
    [[ "$1" == "${greater_version}" ]] && return 0 || return 1
}

cp -f "${script_dir}/../scripts/cloudron-support" /usr/bin/cloudron-support
cp -f "${script_dir}/../scripts/cloudron-translation-update" /usr/bin/cloudron-translation-update
rm -f /usr/bin/cloudron-logs # legacy script

# this needs to match the cloudron/base:4.2.0 gid
if ! getent group media; then
    addgroup --gid 500 --system media
fi

log "Configuring docker"
cp "${script_dir}/start/docker-cloudron-app.apparmor" /etc/apparmor.d/docker-cloudron-app
systemctl enable apparmor
systemctl restart apparmor

usermod ${USER} -a -G docker

if ! grep -q ip6tables /etc/systemd/system/docker.service.d/cloudron.conf; then
    log "Adding ip6tables flag to docker" # https://github.com/moby/moby/pull/41622
    echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2 --experimental --ip6tables --userland-proxy=false" > /etc/systemd/system/docker.service.d/cloudron.conf
    systemctl daemon-reload
    systemctl restart docker
fi

if ! grep -q userland-proxy /etc/systemd/system/docker.service.d/cloudron.conf; then
    log "Adding userland-proxy=false to docker" # https://github.com/moby/moby/pull/41622
    echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2 --experimental --ip6tables --userland-proxy=false" > /etc/systemd/system/docker.service.d/cloudron.conf
    systemctl daemon-reload
    systemctl restart docker
fi

mkdir -p "${BOX_DATA_DIR}" "${APPS_DATA_DIR}" "${MAIL_DATA_DIR}"

# keep these in sync with paths.js
log "Ensuring directories"

mkdir -p "${PLATFORM_DATA_DIR}/"{graphite,mysql,postgresql,mongodb,redis,tls,logrotate.d,acme,backup,update,firewall,sshfs,cifs,oidc,diskusage,source-archives,persistent}
mkdir -p "${PLATFORM_DATA_DIR}/addons/mail/"{banner,dkim}
mkdir -p "${PLATFORM_DATA_DIR}/logs/"{backup,updater,tasks}
mkdir -p "${PLATFORM_DATA_DIR}/sftp/ssh" # sftp keys

# ensure backups folder exists and is writeable
mkdir -p /var/backups
chmod 777 /var/backups

log "Configuring journald"
sed -e "s/^#SystemMaxUse=.*$/SystemMaxUse=100M/" \
    -e "s/^#ForwardToSyslog=.*$/ForwardToSyslog=no/" \
    -i /etc/systemd/journald.conf

# When rotating logs, systemd kills journald too soon sometimes
# See https://github.com/systemd/systemd/issues/1353 (this is upstream default)
sed -e "s/^WatchdogSec=.*$/WatchdogSec=3min/" \
    -i /lib/systemd/system/systemd-journald.service

usermod -a -G systemd-journal ${USER} # Give user access to system logs
if [[ ! -d /var/log/journal ]]; then # in some images, this directory is not created making system log to /run/systemd instead
    mkdir -p /var/log/journal
    chown root:systemd-journal /var/log/journal
    chmod g+s /var/log/journal  # sticky bit for group propagation
fi
systemctl daemon-reload
systemctl restart systemd-journald

# Give user access to nginx logs (uses adm group)
usermod -a -G adm ${USER}

log "Setting up unbound"
rm -f /etc/unbound/unbound.conf.d/prefer-ip4.conf # old config file
cp -f "${script_dir}/start/unbound/unbound.conf" /etc/unbound/unbound.conf.d/cloudron-network.conf
if [[ "${ubuntu_version}" == "20.04" || "${ubuntu_version}" == "22.04" ]]; then
    # on older ubuntu, prefer-ip4 option does not exist. do-ip6 has to be disabled because SpamHaus rejects IPv6 queries
    # this means we cannot support IPv6 only servers on older ubuntu
    sed -e 's/do-ip6: yes/do-ip6: no/' -e 's/prefer-ip4:/# prefer-ip4:/' -i /etc/unbound/unbound.conf.d/cloudron-network.conf
fi
rm -f /etc/unbound/unbound.conf.d/remote-control.conf # on ubuntu 24

log "Adding systemd services"
cp -r "${script_dir}/start/systemd/." /etc/systemd/system/
systemctl daemon-reload
systemctl enable unbound
systemctl enable box
systemctl enable cloudron-firewall
systemctl enable --now cloudron-disable-thp

# update firewall rules. this must be done after docker created it's rules
systemctl restart cloudron-firewall

# For logrotate
systemctl enable --now cron

# ensure unbound runs
systemctl restart unbound

# nfs-common depends on rpcbind which is only needed for NFS v2/v3 . can be removed after 8.0.1 . systemctl list-sockets | grep 111
systemctl disable rpcbind.socket rpcbind.service || true
systemctl stop rpcbind.socket rpcbind.service || true

log "Configuring sudoers"
rm -f /etc/sudoers.d/${USER} /etc/sudoers.d/cloudron
cp "${script_dir}/start/sudoers" /etc/sudoers.d/cloudron

# can be removed after 9.0
log "Unconfiguring collectd"
rm -rf "${PLATFORM_DATA_DIR}/collectd"
systemctl disable collectd || true
systemctl stop collectd || true

log "Configuring logrotate"
if ! grep -q "^include ${PLATFORM_DATA_DIR}/logrotate.d" /etc/logrotate.conf; then
    echo -e "\ninclude ${PLATFORM_DATA_DIR}/logrotate.d\n" >> /etc/logrotate.conf
fi
cp "${script_dir}/start/logrotate/"* "${PLATFORM_DATA_DIR}/logrotate.d/"

# logrotate files have to be owned by root, this is here to fixup existing installations where we were resetting the owner to yellowtent
chown root:root "${PLATFORM_DATA_DIR}/logrotate.d/"

log "Adding motd message for admins"
cp "${script_dir}/start/cloudron-motd" /etc/update-motd.d/92-cloudron

log "Configuring nginx"
# link nginx config to system config
unlink /etc/nginx 2>/dev/null || rm -rf /etc/nginx
ln -s "${PLATFORM_DATA_DIR}/nginx" /etc/nginx
mkdir -p "${PLATFORM_DATA_DIR}/nginx/applications/dashboard"
mkdir -p "${PLATFORM_DATA_DIR}/nginx/cert"
cp "${script_dir}/start/nginx/nginx.conf" "${PLATFORM_DATA_DIR}/nginx/nginx.conf"
cp "${script_dir}/start/nginx/mime.types" "${PLATFORM_DATA_DIR}/nginx/mime.types"
touch "${PLATFORM_DATA_DIR}/nginx/trusted.ips"
if ! grep -q "^Restart=" /etc/systemd/system/multi-user.target.wants/nginx.service; then
    # default nginx service file does not restart on crash
    echo -e "\n[Service]\nRestart=always\n" >> /etc/systemd/system/multi-user.target.wants/nginx.service
fi

# worker_rlimit_nofile in nginx config can be max this number
mkdir -p /etc/systemd/system/nginx.service.d
if ! grep -q "^LimitNOFILE=" /etc/systemd/system/nginx.service.d/cloudron.conf 2>/dev/null; then
    echo -e "[Service]\nLimitNOFILE=16384\n" > /etc/systemd/system/nginx.service.d/cloudron.conf
fi

systemctl daemon-reload
systemctl start nginx

# restart mysql to make sure it has latest config
if [[ ! -f /etc/mysql/mysql.cnf ]] || ! diff -q "${script_dir}/start/mysql.cnf" /etc/mysql/mysql.cnf >/dev/null; then
    # wait for all running mysql jobs
    cp "${script_dir}/start/mysql.cnf" /etc/mysql/mysql.cnf
    while true; do
        if ! systemctl list-jobs | grep mysql; then break; fi
        log "Waiting for mysql jobs..."
        sleep 1
    done
    log "Stopping mysql"
    systemctl stop mysql
    while mysqladmin ping 2>/dev/null; do
        log "Waiting for mysql to stop..."
        sleep 1
    done
fi

# the start/stop of mysql is separate to make sure it got reloaded with latest config and it's up and running before we start the new box code
# when using 'system restart mysql', it seems to restart much later and the box code loses connection during platform startup (dangerous!)
log "Starting mysql"
systemctl start mysql
while ! mysqladmin ping 2>/dev/null; do
    log "Waiting for mysql to start..."
    sleep 1
done

readonly mysql_root_password="password"
mysqladmin -u root -ppassword password password # reset default root password
readonly mysqlVersion=$(mysql -NB -u root -p${mysql_root_password} -e 'SELECT VERSION()' 2>/dev/null)
if [[ "${mysqlVersion}" == "8.0."* ]]; then
    # mysql 8 added a new caching_sha2_password scheme which mysqljs does not support
    mysql -u root -p${mysql_root_password} -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '${mysql_root_password}';"
fi
mysql -u root -p${mysql_root_password} -e 'CREATE DATABASE IF NOT EXISTS box'

# set HOME explicity, because it's not set when the installer calls it. this is done because
# paths.js uses this env var and some of the migrate code requires box code
log "Migrating data"
cd "${BOX_SRC_DIR}"
if ! HOME=${HOME_DIR} BOX_ENV=cloudron DATABASE_URL=mysql://root:${mysql_root_password}@127.0.0.1/box "${BOX_SRC_DIR}/node_modules/.bin/db-migrate" up; then
    log "DB migration failed"
    exit 1
fi

# migrate disk usage cache file
[[ -f "${PLATFORM_DATA_DIR}/diskusage.json" ]] && mv "${PLATFORM_DATA_DIR}/diskusage.json" "${PLATFORM_DATA_DIR}/diskusage/cache.json"

# this file is obsolete now, moved to apps database
rm -f "${PLATFORM_DATA_DIR}/update/updatechecker.json"

log "Changing ownership"
# note, change ownership after db migrate. this allow db migrate to move files around as root and then we can fix it up here
# be careful of what is chown'ed here. subdirs like mysql,redis etc are owned by the containers and will stop working if perms change
chown -R "${USER}" /etc/cloudron
chown "${USER}:${USER}" -R "${PLATFORM_DATA_DIR}/"{nginx,addons,acme,backup,logs,update,sftp,firewall,sshfs,cifs,tls,oidc,diskusage,source-archives,persistent}
chown "${USER}:${USER}" "${PLATFORM_DATA_DIR}/INFRA_VERSION" 2>/dev/null || true
chown "${USER}:${USER}" "${PLATFORM_DATA_DIR}"
chown "${USER}:${USER}" "${APPS_DATA_DIR}"

chown "${USER}:${USER}" -R "${BOX_DATA_DIR}"
# do not chown the boxdata/mail directory entirely; dovecot gets upset
chown "${USER}:${USER}" "${MAIL_DATA_DIR}"

# this require logs dir to have correct permissions
log "Starting cloudron-syslog"
systemctl enable cloudron-syslog
systemctl start cloudron-syslog

log "Starting Cloudron"
systemctl start box

sleep 2 # give systemd sometime to start the processes

log "Almost done"
