diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..5106bf894 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,239 @@ +ARG BASE_VERSION=24.04 +ARG BASE_IMAGE=ubuntu:$BASE_VERSION + +FROM ${BASE_IMAGE} AS documentserver +LABEL maintainer="ONLYOFFICE " +LABEL version="9.2.0" +LABEL description="ONLYOFFICE DocumentServer for ARM64 and AMD64" + +ARG BASE_VERSION +ARG PG_VERSION=16 +ARG PACKAGE_SUFFIX=t64 + +# Oracle Instant Client versions +ENV OC_RELEASE_NUM=23 +ENV OC_RU_VER=7 +ENV OC_RU_REVISION_VER=0 +ENV OC_RESERVED_NUM=25 +ENV OC_RU_DATE=01 +ENV OC_PATH=${OC_RELEASE_NUM}${OC_RU_VER}0000 +ENV OC_FILE_SUFFIX=${OC_RELEASE_NUM}.${OC_RU_VER}.${OC_RU_REVISION_VER}.${OC_RESERVED_NUM}.${OC_RU_DATE} +ENV OC_VER_DIR=${OC_RELEASE_NUM}_${OC_RU_VER} +ENV OC_DOWNLOAD_URL=https://download.oracle.com/otn_software/linux/instantclient/${OC_PATH} + +# Locale and environment +ENV LANG=en_US.UTF-8 \ + LANGUAGE=en_US:en \ + LC_ALL=en_US.UTF-8 \ + DEBIAN_FRONTEND=noninteractive \ + PG_VERSION=${PG_VERSION} \ + BASE_VERSION=${BASE_VERSION} + +# Company and product info +ARG COMPANY_NAME=onlyoffice +ARG PRODUCT_NAME=documentserver +ARG PRODUCT_EDITION= +ARG PACKAGE_VERSION=9.2.0 +ARG TARGETARCH +ARG PACKAGE_BASEURL="http://download.onlyoffice.com/install/documentserver/linux" + +ENV COMPANY_NAME=$COMPANY_NAME \ + PRODUCT_NAME=$PRODUCT_NAME \ + PRODUCT_EDITION=$PRODUCT_EDITION \ + DS_PLUGIN_INSTALLATION=false \ + DS_DOCKER_INSTALLATION=true + +# ============================================ +# HARDCODED CONFIGURATION - NO ENV VARS NEEDED +# ============================================ +# Database Configuration +ENV DB_TYPE=postgres \ + DB_HOST=localhost \ + DB_PORT=5432 \ + DB_NAME=onlyoffice \ + DB_USER=onlyoffice \ + DB_PWD=onlyoffice + +# Redis Configuration +ENV REDIS_SERVER_HOST=localhost \ + REDIS_SERVER_PORT=6379 \ + REDIS_ENABLED=true + +# RabbitMQ Configuration +ENV AMQP_SERVER_HOST=localhost \ + AMQP_SERVER_PORT=5672 \ + AMQP_SERVER_USER=guest \ + AMQP_SERVER_PWD=guest \ + AMQP_SERVER_PROTO=amqp + +# JWT Configuration (disabled by default) +ENV JWT_ENABLED=false \ + JWT_SECRET=secret \ + JWT_HEADER=Authorization \ + JWT_IN_BODY=false + +# Other settings +ENV WOPI_ENABLED=false \ + GENERATE_FONTS=true \ + METRICS_ENABLED=false \ + PLUGINS_ENABLED=true \ + DS_LOG_LEVEL=WARN + +# Create policy to allow services to start +RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d + +# Install base packages +RUN apt-get -y update && \ + apt-get -yq install \ + wget \ + apt-transport-https \ + gnupg \ + locales \ + lsb-release && \ + locale-gen en_US.UTF-8 + +# Add Microsoft repository for SQL tools +RUN wget -q -O /etc/apt/sources.list.d/mssql-release.list \ + "https://packages.microsoft.com/config/ubuntu/$BASE_VERSION/prod.list" && \ + wget -q -O /tmp/microsoft.asc https://packages.microsoft.com/keys/microsoft.asc && \ + apt-key add /tmp/microsoft.asc && \ + gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg < /tmp/microsoft.asc + +# Accept Microsoft fonts EULA +RUN echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections + +# Install all required packages +RUN apt-get -y update && \ + ACCEPT_EULA=Y apt-get -yq install \ + adduser \ + apt-utils \ + bomstrip \ + certbot \ + cron \ + curl \ + htop \ + libaio1${PACKAGE_SUFFIX} \ + libasound2${PACKAGE_SUFFIX} \ + libboost-regex-dev \ + libcairo2 \ + libcurl3-gnutls \ + libcurl4 \ + libgtk-3-0 \ + libnspr4 \ + libnss3 \ + libstdc++6 \ + libxml2 \ + libxss1 \ + libxtst6 \ + mssql-tools18 \ + mysql-client \ + nano \ + net-tools \ + netcat-openbsd \ + nginx-extras \ + postgresql \ + postgresql-client \ + pwgen \ + rabbitmq-server \ + redis-server \ + sudo \ + supervisor \ + ttf-mscorefonts-installer \ + unixodbc-dev \ + unzip \ + xvfb \ + xxd \ + zlib1g || dpkg --configure -a + +# Verify Microsoft fonts installation +RUN if [ $(find /usr/share/fonts/truetype/msttcorefonts -maxdepth 1 -type f -iname '*.ttf' | wc -l) -lt 30 ]; then \ + echo 'msttcorefonts failed to download'; exit 1; \ + fi + +# Configure RabbitMQ for single CPU +RUN echo "SERVER_ADDITIONAL_ERL_ARGS=\"+S 1:1\"" | tee -a /etc/rabbitmq/rabbitmq-env.conf + +# Configure Redis to bind to localhost only +RUN sed -i "s/bind .*/bind 127.0.0.1/g" /etc/redis/redis.conf + +# Add wasm mime type to nginx +RUN sed 's|\(application\/zip.*\)|\1\n application\/wasm wasm;|' -i /etc/nginx/mime.types + +# Configure PostgreSQL +RUN pg_conftool $PG_VERSION main set listen_addresses 'localhost' + +# Start PostgreSQL and create user/database +RUN service postgresql restart && \ + sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH password '${DB_PWD}';" && \ + sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" + +# Download and install Oracle Instant Client (architecture-specific) +RUN wget -O basic.zip ${OC_DOWNLOAD_URL}/instantclient-basic-linux.$(dpkg --print-architecture | sed 's/amd64/x64/')-${OC_FILE_SUFFIX}.zip && \ + wget -O sqlplus.zip ${OC_DOWNLOAD_URL}/instantclient-sqlplus-linux.$(dpkg --print-architecture | sed 's/amd64/x64/')-${OC_FILE_SUFFIX}.zip && \ + unzip -o basic.zip -d /usr/share && \ + unzip -o sqlplus.zip -d /usr/share && \ + mv /usr/share/instantclient_${OC_VER_DIR} /usr/share/instantclient && \ + find /usr/lib /lib -name "libaio.so.1$PACKAGE_SUFFIX" -exec bash -c 'ln -sf "$0" "$(dirname "$0")/libaio.so.1"' {} \; && \ + rm -f basic.zip sqlplus.zip + +# Stop services that were started during build +RUN service postgresql stop && \ + service redis-server stop && \ + service rabbitmq-server stop && \ + service supervisor stop && \ + service nginx stop + +# Clean up apt cache +RUN rm -rf /var/lib/apt/lists/* + +# Copy configuration files +COPY config/supervisor/supervisor /etc/init.d/ +COPY config/supervisor/ds/*.conf /etc/supervisor/conf.d/ +COPY run-document-server.sh /app/ds/run-document-server.sh + +# Create sqlplus wrapper +RUN mkdir -p /usr/bin && \ + echo '#!/bin/bash' > /usr/bin/sqlplus && \ + echo 'export LD_LIBRARY_PATH=/usr/share/instantclient:$LD_LIBRARY_PATH' >> /usr/bin/sqlplus && \ + echo 'exec /usr/share/instantclient/sqlplus "$@"' >> /usr/bin/sqlplus && \ + chmod +x /usr/bin/sqlplus + +# Expose ports +EXPOSE 80 443 + +# Download and install ONLYOFFICE DocumentServer package +RUN PACKAGE_FILE="${COMPANY_NAME}-${PRODUCT_NAME}${PRODUCT_EDITION}${PACKAGE_VERSION:+_$PACKAGE_VERSION}_${TARGETARCH:-$(dpkg --print-architecture)}.deb" && \ + echo "Downloading package: $PACKAGE_BASEURL/$PACKAGE_FILE" && \ + wget -q -P /tmp "$PACKAGE_BASEURL/$PACKAGE_FILE" && \ + apt-get -y update && \ + service postgresql start && \ + apt-get -yq install /tmp/$PACKAGE_FILE && \ + # Clean up temp database used during installation + PGPASSWORD=$DB_PWD dropdb -h localhost -p 5432 -U $DB_USER $DB_NAME 2>/dev/null || true && \ + sudo -u postgres psql -c "DROP ROLE IF EXISTS ${DB_USER};" 2>/dev/null || true && \ + service postgresql stop && \ + chmod 755 /etc/init.d/supervisor && \ + sed "s/COMPANY_NAME/${COMPANY_NAME}/g" -i /etc/supervisor/conf.d/*.conf && \ + service supervisor stop && \ + chmod 755 /app/ds/*.sh && \ + # Add GO command for MSSQL scripts + printf "\nGO" >> "/var/www/$COMPANY_NAME/documentserver/server/schema/mssql/createdb.sql" && \ + printf "\nGO" >> "/var/www/$COMPANY_NAME/documentserver/server/schema/mssql/removetbl.sql" && \ + # Add exit command for Oracle scripts + printf "\nexit" >> "/var/www/$COMPANY_NAME/documentserver/server/schema/oracle/createdb.sql" && \ + printf "\nexit" >> "/var/www/$COMPANY_NAME/documentserver/server/schema/oracle/removetbl.sql" && \ + rm -f /tmp/$PACKAGE_FILE && \ + rm -rf /var/log/$COMPANY_NAME && \ + rm -rf /var/lib/apt/lists/* + +# Define volumes for persistent data +VOLUME /var/log/$COMPANY_NAME \ + /var/lib/$COMPANY_NAME \ + /var/www/$COMPANY_NAME/Data \ + /var/lib/postgresql \ + /var/lib/rabbitmq \ + /var/lib/redis \ + /usr/share/fonts/truetype/custom + +# Set entrypoint +ENTRYPOINT ["/app/ds/run-document-server.sh"] diff --git a/config/supervisor/ds/ds-converter.conf b/config/supervisor/ds/ds-converter.conf new file mode 100644 index 000000000..7c3238466 --- /dev/null +++ b/config/supervisor/ds/ds-converter.conf @@ -0,0 +1,18 @@ +[program:ds-converter] +command=/var/www/COMPANY_NAME/documentserver/server/FileConverter/converter +directory=/var/www/COMPANY_NAME/documentserver/server/FileConverter +user=ds +autostart=true +autorestart=true +startsecs=10 +startretries=3 +stopwaitsecs=60 +killasgroup=true +stopasgroup=true +stdout_logfile=/var/log/COMPANY_NAME/documentserver/converter/out.log +stderr_logfile=/var/log/COMPANY_NAME/documentserver/converter/err.log +stdout_logfile_maxbytes=10MB +stderr_logfile_maxbytes=10MB +stdout_logfile_backups=5 +stderr_logfile_backups=5 +environment=NODE_ENV="production-linux",NODE_CONFIG_DIR="/etc/COMPANY_NAME/documentserver",NODE_DISABLE_COLORS="1" diff --git a/config/supervisor/ds/ds-docservice.conf b/config/supervisor/ds/ds-docservice.conf new file mode 100644 index 000000000..7e7aa4424 --- /dev/null +++ b/config/supervisor/ds/ds-docservice.conf @@ -0,0 +1,18 @@ +[program:ds-docservice] +command=/var/www/COMPANY_NAME/documentserver/server/DocService/docservice +directory=/var/www/COMPANY_NAME/documentserver/server/DocService +user=ds +autostart=true +autorestart=true +startsecs=10 +startretries=3 +stopwaitsecs=60 +killasgroup=true +stopasgroup=true +stdout_logfile=/var/log/COMPANY_NAME/documentserver/docservice/out.log +stderr_logfile=/var/log/COMPANY_NAME/documentserver/docservice/err.log +stdout_logfile_maxbytes=10MB +stderr_logfile_maxbytes=10MB +stdout_logfile_backups=5 +stderr_logfile_backups=5 +environment=NODE_ENV="production-linux",NODE_CONFIG_DIR="/etc/COMPANY_NAME/documentserver",NODE_DISABLE_COLORS="1" diff --git a/config/supervisor/ds/ds-metrics.conf b/config/supervisor/ds/ds-metrics.conf new file mode 100644 index 000000000..1ba4fc8df --- /dev/null +++ b/config/supervisor/ds/ds-metrics.conf @@ -0,0 +1,18 @@ +[program:ds-metrics] +command=/var/www/COMPANY_NAME/documentserver/server/Metrics/metrics +directory=/var/www/COMPANY_NAME/documentserver/server/Metrics +user=ds +autostart=false +autorestart=true +startsecs=10 +startretries=3 +stopwaitsecs=10 +killasgroup=true +stopasgroup=true +stdout_logfile=/var/log/COMPANY_NAME/documentserver/metrics/out.log +stderr_logfile=/var/log/COMPANY_NAME/documentserver/metrics/err.log +stdout_logfile_maxbytes=10MB +stderr_logfile_maxbytes=10MB +stdout_logfile_backups=5 +stderr_logfile_backups=5 +environment=NODE_ENV="production-linux",NODE_CONFIG_DIR="/etc/COMPANY_NAME/documentserver",NODE_DISABLE_COLORS="1" diff --git a/config/supervisor/supervisor b/config/supervisor/supervisor new file mode 100644 index 000000000..04434a519 --- /dev/null +++ b/config/supervisor/supervisor @@ -0,0 +1,45 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: supervisor +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Supervisor init script +# Description: Supervisor is a client/server system for controlling +# process state under UNIX-like operating systems. +### END INIT INFO + +SUPERVISORD=/usr/bin/supervisord +SUPERVISORCTL=/usr/bin/supervisorctl +PIDFILE=/var/run/supervisord.pid +CONF=/etc/supervisor/supervisord.conf + +. /lib/lsb/init-functions + +case "$1" in + start) + log_daemon_msg "Starting supervisor" "supervisord" + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $SUPERVISORD -- -c $CONF + log_end_msg $? + ;; + stop) + log_daemon_msg "Stopping supervisor" "supervisord" + start-stop-daemon --stop --quiet --pidfile $PIDFILE + log_end_msg $? + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc -p $PIDFILE $SUPERVISORD supervisor && exit 0 || exit $? + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..4038486f4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,136 @@ +# ONLYOFFICE DocumentServer Docker Compose Configuration +# All services run with hardcoded credentials - no environment variables needed +# Works on both ARM64 and AMD64 architectures + +services: + # =========================================== + # ONLYOFFICE DocumentServer - Main Service + # =========================================== + onlyoffice-documentserver: + build: + context: . + args: + - PACKAGE_VERSION=9.2.0 + image: onlyoffice/documentserver:9.2.0 + container_name: onlyoffice-documentserver + depends_on: + onlyoffice-postgresql: + condition: service_healthy + onlyoffice-rabbitmq: + condition: service_healthy + environment: + # Database Configuration + - DB_TYPE=postgres + - DB_HOST=onlyoffice-postgresql + - DB_PORT=5432 + - DB_NAME=onlyoffice + - DB_USER=onlyoffice + - DB_PWD=onlyoffice + # RabbitMQ Configuration + - AMQP_URI=amqp://guest:guest@onlyoffice-rabbitmq:5672 + - AMQP_SERVER_HOST=onlyoffice-rabbitmq + - AMQP_SERVER_PORT=5672 + - AMQP_SERVER_USER=guest + - AMQP_SERVER_PWD=guest + # Redis Configuration (using built-in Redis) + - REDIS_SERVER_HOST=localhost + - REDIS_SERVER_PORT=6379 + - REDIS_ENABLED=true + # JWT Configuration (disabled for simplicity) + - JWT_ENABLED=false + - JWT_SECRET=secret + - JWT_HEADER=Authorization + - JWT_IN_BODY=false + # Other settings + - WOPI_ENABLED=false + - GENERATE_FONTS=true + - PLUGINS_ENABLED=true + - DS_LOG_LEVEL=WARN + ports: + - '80:80' + - '443:443' + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost/healthcheck"] + interval: 30s + retries: 5 + start_period: 120s + timeout: 10s + stdin_open: true + restart: always + stop_grace_period: 60s + volumes: + - ds_data:/var/www/onlyoffice/Data + - ds_logs:/var/log/onlyoffice + - ds_cache:/var/lib/onlyoffice/documentserver/App_Data/cache/files + - ds_fonts:/usr/share/fonts/truetype/custom + + # =========================================== + # PostgreSQL Database + # =========================================== + onlyoffice-postgresql: + container_name: onlyoffice-postgresql + image: postgres:16 + environment: + - POSTGRES_DB=onlyoffice + - POSTGRES_USER=onlyoffice + - POSTGRES_PASSWORD=onlyoffice + - POSTGRES_HOST_AUTH_METHOD=md5 + restart: always + expose: + - '5432' + volumes: + - postgresql_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U onlyoffice -d onlyoffice"] + interval: 10s + retries: 5 + start_period: 10s + timeout: 5s + + # =========================================== + # RabbitMQ Message Broker + # =========================================== + onlyoffice-rabbitmq: + container_name: onlyoffice-rabbitmq + image: rabbitmq:3-management + hostname: onlyoffice-rabbitmq + environment: + - RABBITMQ_DEFAULT_USER=guest + - RABBITMQ_DEFAULT_PASS=guest + restart: always + expose: + - '5672' + - '15672' + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "status"] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + +# =========================================== +# Persistent Volumes +# =========================================== +volumes: + postgresql_data: + driver: local + rabbitmq_data: + driver: local + ds_data: + driver: local + ds_logs: + driver: local + ds_cache: + driver: local + ds_fonts: + driver: local + +# =========================================== +# Network Configuration +# =========================================== +networks: + default: + name: onlyoffice-network + driver: bridge diff --git a/run-document-server.sh b/run-document-server.sh new file mode 100644 index 000000000..4c653cd1e --- /dev/null +++ b/run-document-server.sh @@ -0,0 +1,359 @@ +#!/bin/bash +# ONLYOFFICE DocumentServer Startup Script +# This script initializes and starts all required services + +umask 0022 + +# ============================================ +# HARDCODED CONFIGURATION +# ============================================ +COMPANY_NAME=${COMPANY_NAME:-onlyoffice} +PRODUCT_NAME=${PRODUCT_NAME:-documentserver} + +# Database Configuration +DB_TYPE=${DB_TYPE:-postgres} +DB_HOST=${DB_HOST:-localhost} +DB_PORT=${DB_PORT:-5432} +DB_NAME=${DB_NAME:-onlyoffice} +DB_USER=${DB_USER:-onlyoffice} +DB_PWD=${DB_PWD:-onlyoffice} + +# Redis Configuration +REDIS_SERVER_HOST=${REDIS_SERVER_HOST:-localhost} +REDIS_SERVER_PORT=${REDIS_SERVER_PORT:-6379} +REDIS_ENABLED=${REDIS_ENABLED:-true} + +# RabbitMQ Configuration +AMQP_SERVER_HOST=${AMQP_SERVER_HOST:-localhost} +AMQP_SERVER_PORT=${AMQP_SERVER_PORT:-5672} +AMQP_SERVER_USER=${AMQP_SERVER_USER:-guest} +AMQP_SERVER_PWD=${AMQP_SERVER_PWD:-guest} +AMQP_SERVER_PROTO=${AMQP_SERVER_PROTO:-amqp} + +# JWT Configuration +JWT_ENABLED=${JWT_ENABLED:-false} +JWT_SECRET=${JWT_SECRET:-secret} +JWT_HEADER=${JWT_HEADER:-Authorization} +JWT_IN_BODY=${JWT_IN_BODY:-false} + +# Other settings +WOPI_ENABLED=${WOPI_ENABLED:-false} +GENERATE_FONTS=${GENERATE_FONTS:-true} +PLUGINS_ENABLED=${PLUGINS_ENABLED:-true} +DS_LOG_LEVEL=${DS_LOG_LEVEL:-WARN} +NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-1} + +# ============================================ +# DIRECTORIES +# ============================================ +APP_DIR="/var/www/${COMPANY_NAME}/documentserver" +DATA_DIR="/var/www/${COMPANY_NAME}/Data" +PRIVATE_DATA_DIR="${DATA_DIR}/.private" +LOG_DIR="/var/log/${COMPANY_NAME}" +DS_LOG_DIR="${LOG_DIR}/documentserver" +LIB_DIR="/var/lib/${COMPANY_NAME}" +DS_LIB_DIR="${LIB_DIR}/documentserver" +CONF_DIR="/etc/${COMPANY_NAME}/documentserver" +PG_ROOT="/var/lib/postgresql" +PGDATA="${PG_ROOT}/${PG_VERSION:-16}/main" +REDIS_DATA="/var/lib/redis" +RABBITMQ_DATA="/var/lib/rabbitmq" + +# ============================================ +# FUNCTIONS +# ============================================ + +start_process() { + "$@" & + CHILD=$! + wait "$CHILD" + CHILD="" +} + +clean_exit() { + [[ -z "$CHILD" ]] || kill -s SIGTERM "$CHILD" 2>/dev/null + /usr/bin/documentserver-prepare4shutdown.sh 2>/dev/null || true + exit +} + +trap clean_exit SIGTERM SIGQUIT SIGABRT SIGINT + +waiting_for_db() { + echo "Waiting for database connection..." + local RETRY=0 + local MAX_RETRY=30 + while ! PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" -c "SELECT 1" >/dev/null 2>&1; do + RETRY=$((RETRY + 1)) + if [ $RETRY -ge $MAX_RETRY ]; then + echo "ERROR: Database connection failed after ${MAX_RETRY} attempts" + exit 1 + fi + echo " Attempt ${RETRY}/${MAX_RETRY}..." + sleep 2 + done + echo "Database connection established!" +} + +waiting_for_amqp() { + echo "Waiting for RabbitMQ connection..." + local RETRY=0 + local MAX_RETRY=30 + while ! nc -z "${AMQP_SERVER_HOST}" "${AMQP_SERVER_PORT}" 2>/dev/null; do + RETRY=$((RETRY + 1)) + if [ $RETRY -ge $MAX_RETRY ]; then + echo "ERROR: RabbitMQ connection failed after ${MAX_RETRY} attempts" + exit 1 + fi + echo " Attempt ${RETRY}/${MAX_RETRY}..." + sleep 2 + done + echo "RabbitMQ connection established!" +} + +waiting_for_redis() { + echo "Waiting for Redis connection..." + local RETRY=0 + local MAX_RETRY=30 + while ! nc -z "${REDIS_SERVER_HOST}" "${REDIS_SERVER_PORT}" 2>/dev/null; do + RETRY=$((RETRY + 1)) + if [ $RETRY -ge $MAX_RETRY ]; then + echo "ERROR: Redis connection failed after ${MAX_RETRY} attempts" + exit 1 + fi + echo " Attempt ${RETRY}/${MAX_RETRY}..." + sleep 2 + done + echo "Redis connection established!" +} + +create_postgresql_cluster() { + echo "Creating PostgreSQL cluster..." + sudo -u postgres /usr/lib/postgresql/${PG_VERSION:-16}/bin/initdb -D "${PGDATA}" +} + +create_postgresql_db() { + echo "Creating PostgreSQL database..." + sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH password '${DB_PWD}';" 2>/dev/null || true + sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" 2>/dev/null || true +} + +create_postgresql_tbl() { + echo "Creating database tables..." + PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" \ + -f "${APP_DIR}/server/schema/postgresql/createdb.sql" 2>/dev/null || true +} + +update_ds_settings() { + echo "Updating DocumentServer settings..." + local JSON="${CONF_DIR}/local.json" + + # Create local.json with all settings + cat > "${JSON}" << EOF +{ + "services": { + "CoAuthoring": { + "sql": { + "type": "${DB_TYPE}", + "dbHost": "${DB_HOST}", + "dbPort": "${DB_PORT}", + "dbName": "${DB_NAME}", + "dbUser": "${DB_USER}", + "dbPass": "${DB_PWD}" + }, + "redis": { + "host": "${REDIS_SERVER_HOST}", + "port": ${REDIS_SERVER_PORT} + }, + "pubsub": { + "maxNumberOfReceivers": "infinity" + }, + "token": { + "enable": { + "request": { + "inbox": ${JWT_ENABLED}, + "outbox": ${JWT_ENABLED} + }, + "browser": ${JWT_ENABLED} + }, + "inbox": { + "header": "${JWT_HEADER}", + "inBody": ${JWT_IN_BODY} + }, + "outbox": { + "header": "${JWT_HEADER}", + "inBody": ${JWT_IN_BODY} + } + }, + "secret": { + "inbox": { + "string": "${JWT_SECRET}" + }, + "outbox": { + "string": "${JWT_SECRET}" + }, + "session": { + "string": "${JWT_SECRET}" + } + } + } + }, + "rabbitmq": { + "url": "${AMQP_SERVER_PROTO}://${AMQP_SERVER_USER}:${AMQP_SERVER_PWD}@${AMQP_SERVER_HOST}:${AMQP_SERVER_PORT}" + }, + "wopi": { + "enable": ${WOPI_ENABLED} + } +} +EOF + + chown ds:ds "${JSON}" + chmod 644 "${JSON}" +} + +# ============================================ +# MAIN STARTUP SEQUENCE +# ============================================ + +echo "============================================" +echo "ONLYOFFICE DocumentServer Startup" +echo "============================================" +echo "Database: ${DB_HOST}:${DB_PORT}/${DB_NAME}" +echo "Redis: ${REDIS_SERVER_HOST}:${REDIS_SERVER_PORT}" +echo "RabbitMQ: ${AMQP_SERVER_HOST}:${AMQP_SERVER_PORT}" +echo "JWT Enabled: ${JWT_ENABLED}" +echo "============================================" + +# Create log directories +for i in converter docservice metrics adminpanel; do + mkdir -p "$DS_LOG_DIR/$i" && touch "$DS_LOG_DIR/$i"/{out,err}.log +done +mkdir -p "${DS_LOG_DIR}-example" && touch "${DS_LOG_DIR}-example"/{out,err}.log + +# Create app directories +for i in ${DS_LIB_DIR}/App_Data/cache/files ${DS_LIB_DIR}/App_Data/docbuilder ${DS_LIB_DIR}-example/files; do + mkdir -p "$i" +done + +# Create data directory +mkdir -p "${DATA_DIR}" +mkdir -p "${PRIVATE_DATA_DIR}" + +# Set permissions +chown ds:ds "${DATA_DIR}" +for i in ${DS_LOG_DIR} ${DS_LOG_DIR}-example ${LIB_DIR}; do + chown -R ds:ds "$i" + chmod -R 755 "$i" +done + +# Array to track local services that need to be started +LOCAL_SERVICES=() + +# Handle database +if [ "${DB_HOST}" = "localhost" ]; then + echo "Starting local PostgreSQL..." + chown -R postgres:postgres ${PG_ROOT} + chmod -R 700 ${PG_ROOT} + + if [ ! -d "${PGDATA}" ]; then + create_postgresql_cluster + PG_NEW_CLUSTER=true + fi + LOCAL_SERVICES+=("postgresql") +fi + +# Handle RabbitMQ +if [ "${AMQP_SERVER_HOST}" = "localhost" ]; then + echo "Starting local RabbitMQ..." + chown -R rabbitmq:rabbitmq ${RABBITMQ_DATA} + chmod -R go=rX,u=rwX ${RABBITMQ_DATA} + if [ -f ${RABBITMQ_DATA}/.erlang.cookie ]; then + chmod 400 ${RABBITMQ_DATA}/.erlang.cookie + fi + rm -rf /var/run/rabbitmq + LOCAL_SERVICES+=("rabbitmq-server") +fi + +# Handle Redis +if [ "${REDIS_ENABLED}" = "true" ] && [ "${REDIS_SERVER_HOST}" = "localhost" ]; then + echo "Starting local Redis..." + chown -R redis:redis ${REDIS_DATA} + chmod -R 750 ${REDIS_DATA} + LOCAL_SERVICES+=("redis-server") +fi + +# Start local services +for i in ${LOCAL_SERVICES[@]}; do + echo "Starting service: $i" + service $i start +done + +# Wait for and initialize database +if [ "${DB_HOST}" = "localhost" ]; then + sleep 3 + if [ "${PG_NEW_CLUSTER}" = "true" ]; then + create_postgresql_db + create_postgresql_tbl + fi +else + waiting_for_db + # Check if tables exist, if not create them + PGPASSWORD="${DB_PWD}" psql -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" \ + -c "SELECT 1 FROM doc_changes LIMIT 1" 2>/dev/null || create_postgresql_tbl +fi + +# Wait for other services +waiting_for_amqp +if [ "${REDIS_ENABLED}" = "true" ]; then + waiting_for_redis +fi + +# Update DocumentServer configuration +update_ds_settings + +# Configure nginx +echo "Configuring Nginx..." +NGINX_ONLYOFFICE_PATH="${CONF_DIR}/nginx" +NGINX_ONLYOFFICE_CONF="${NGINX_ONLYOFFICE_PATH}/ds.conf" +ln -sf ${NGINX_ONLYOFFICE_PATH}/ds.conf.tmpl ${NGINX_ONLYOFFICE_CONF} + +# Install plugins if enabled +if [ "${PLUGINS_ENABLED}" = "true" ]; then + echo "Installing plugins..." + start_process documentserver-pluginsmanager.sh -r false --update=\"${APP_DIR}/sdkjs-plugins/plugin-list-default.json\" >/dev/null 2>&1 || true + echo "Plugins installed." +fi + +# Start supervisor (manages DocService, Converter, etc.) +echo "Starting Supervisor..." +service supervisor start + +# Start cron for log rotation +service cron start + +# Flush cache +start_process documentserver-flush-cache.sh -r false 2>/dev/null || true + +# Start nginx +echo "Starting Nginx..." +service nginx start + +# Generate fonts if enabled +if [ "${GENERATE_FONTS}" = "true" ]; then + echo "Generating fonts..." + start_process documentserver-generate-allfonts.sh false >/dev/null 2>&1 || true + echo "Fonts generated." +fi + +# Compress static files +start_process documentserver-static-gzip.sh false >/dev/null 2>&1 || true + +echo "============================================" +echo "ONLYOFFICE DocumentServer is ready!" +echo "Access at: http://localhost" +if [ "${JWT_ENABLED}" = "true" ]; then + echo "JWT Secret: ${JWT_SECRET}" +fi +echo "============================================" + +# Keep container running and tail logs +exec tail -F ${DS_LOG_DIR}/docservice/out.log ${DS_LOG_DIR}/converter/out.log 2>/dev/null