393 lines
11 KiB
Bash
393 lines
11 KiB
Bash
#!/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}"
|
|
|
|
# Configure pg_hba.conf to trust local connections
|
|
# This avoids password authentication issues between services running in the same container
|
|
echo "local all onlyoffice trust" > "${PGDATA}/pg_hba.conf"
|
|
echo "local all all peer" >> "${PGDATA}/pg_hba.conf"
|
|
echo "host all all 127.0.0.1/32 trust" >> "${PGDATA}/pg_hba.conf"
|
|
echo "host all all ::1/128 trust" >> "${PGDATA}/pg_hba.conf"
|
|
}
|
|
|
|
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
|
|
|
|
# Configure ds-example if enabled
|
|
if [ "${DS_EXAMPLE}" = "true" ]; then
|
|
echo "Enabling test example service..."
|
|
cat > /etc/supervisor/conf.d/ds-example.conf <<EOF
|
|
[program:ds-example]
|
|
command=/var/www/onlyoffice/documentserver-example/example
|
|
directory=/var/www/onlyoffice/documentserver-example
|
|
user=ds
|
|
autostart=true
|
|
autorestart=true
|
|
startsecs=5
|
|
stopwaitsecs=10
|
|
killasgroup=true
|
|
stopasgroup=true
|
|
stdout_logfile=/var/log/${COMPANY_NAME}/documentserver-example/out.log
|
|
stderr_logfile=/var/log/${COMPANY_NAME}/documentserver-example/err.log
|
|
stdout_logfile_maxbytes=10MB
|
|
stderr_logfile_maxbytes=10MB
|
|
environment=NODE_ENV="production-linux",NODE_CONFIG_DIR="/var/www/onlyoffice/documentserver-example/config"
|
|
EOF
|
|
|
|
# Create log directory for example
|
|
mkdir -p /var/log/${COMPANY_NAME}/documentserver-example
|
|
chown -R ds:ds /var/log/${COMPANY_NAME}/documentserver-example
|
|
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
|