diff --git a/build/install/OneClickInstall/install-RedHat.sh b/build/install/OneClickInstall/install-RedHat.sh new file mode 100644 index 0000000000..656d535882 --- /dev/null +++ b/build/install/OneClickInstall/install-RedHat.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e + +package_manager="yum" +package_sysname="onlyoffice"; + +package_services=""; +RES_APP_INSTALLED="is already installed"; +RES_APP_CHECK_PORTS="uses ports" +RES_CHECK_PORTS="please, make sure that the ports are free."; +RES_INSTALL_SUCCESS="Thank you for installing ONLYOFFICE Appserver."; +RES_QUESTIONS="In case you have any questions contact us via http://support.onlyoffice.com or visit our forum at http://dev.onlyoffice.org" + +while [ "$1" != "" ]; do + case $1 in + + -u | --update ) + if [ "$2" != "" ]; then + UPDATE=$2 + shift + fi + ;; + + -ls | --local_scripts ) + if [ "$2" != "" ]; then + LOCAL_SCRIPTS=$2 + shift + fi + ;; + + -? | -h | --help ) + echo " Usage $0 [PARAMETER] [[PARAMETER], ...]" + echo " Parameters:" + echo " -u, --update use to update existing components (true|false)" + echo " -ls, --local_scripts use 'true' to run local scripts (true|false)" + echo " -?, -h, --help this help" + echo + exit 0 + ;; + + esac + shift +done + +if [ -z "${UPDATE}" ]; then + UPDATE="false"; +fi + +if [ -z "${LOCAL_SCRIPTS}" ]; then + LOCAL_SCRIPTS="true"; +fi + +cat > /etc/yum.repos.d/onlyoffice.repo < /etc/yum.repos.d/onlyoffice4testing.repo </dev/null 2>&1; then + if [ -z $MYSQL_TEMPORARY_ROOT_PASS ]; then + MYSQL="mysql --connect-expired-password -u$MYSQL_SERVER_USER -D mysql"; + else + MYSQL="mysql --connect-expired-password -u$MYSQL_SERVER_USER -p${MYSQL_TEMPORARY_ROOT_PASS} -D mysql"; + fi + + $MYSQL -e "ALTER USER '${MYSQL_SERVER_USER}'@'localhost' IDENTIFIED WITH mysql_native_password BY '${MYSQL_TEMPORARY_ROOT_PASS}'" >/dev/null 2>&1 \ + || $MYSQL -e "UPDATE user SET plugin='mysql_native_password', authentication_string=PASSWORD('${MYSQL_TEMPORARY_ROOT_PASS}') WHERE user='${MYSQL_SERVER_USER}' and host='localhost';" + + systemctl restart mysqld + fi +fi + +if [ -e /etc/redis.conf ]; then + sed -i "s/bind .*/bind 127.0.0.1/g" /etc/redis.conf + sed -r "/^save\s[0-9]+/d" -i /etc/redis.conf + + systemctl restart redis +fi + +sed "/host\s*all\s*all\s*127\.0\.0\.1\/32\s*ident$/s|ident$|trust|" -i /var/lib/pgsql/data/pg_hba.conf +sed "/host\s*all\s*all\s*::1\/128\s*ident$/s|ident$|trust|" -i /var/lib/pgsql/data/pg_hba.conf + +for SVC in $package_services; do + systemctl start $SVC + systemctl enable $SVC +done + +if [ "$DOCUMENT_SERVER_INSTALLED" = "false" ]; then + declare -x DS_PORT=8083 + + DS_RABBITMQ_HOST=localhost; + DS_RABBITMQ_USER=guest; + DS_RABBITMQ_PWD=guest; + + DS_REDIS_HOST=localhost; + + DS_COMMON_NAME=${DS_COMMON_NAME:-"ds"}; + + DS_DB_HOST=localhost; + DS_DB_NAME=$DS_COMMON_NAME; + DS_DB_USER=$DS_COMMON_NAME; + DS_DB_PWD=$DS_COMMON_NAME; + + declare -x JWT_ENABLED=true; + declare -x JWT_SECRET="$(cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 12)"; + declare -x JWT_HEADER="AuthorizationJwt"; + + if ! su - postgres -s /bin/bash -c "psql -lqt" | cut -d \| -f 1 | grep -q ${DS_DB_NAME}; then + su - postgres -s /bin/bash -c "psql -c \"CREATE DATABASE ${DS_DB_NAME};\"" + su - postgres -s /bin/bash -c "psql -c \"CREATE USER ${DS_DB_USER} WITH password '${DS_DB_PWD}';\"" + su - postgres -s /bin/bash -c "psql -c \"GRANT ALL privileges ON DATABASE ${DS_DB_NAME} TO ${DS_DB_USER};\"" + fi + + ${package_manager} -y install ${package_sysname}-documentserver + + systemctl restart supervisord + +expect << EOF + + set timeout -1 + log_user 1 + + spawn documentserver-configure.sh + + expect "Configuring database access..." + + expect -re "Host" + send "\025$DS_DB_HOST\r" + + expect -re "Database name" + send "\025$DS_DB_NAME\r" + + expect -re "User" + send "\025$DS_DB_USER\r" + + expect -re "Password" + send "\025$DS_DB_PWD\r" + + if { "${INSTALLATION_TYPE}" == "ENTERPRISE" || "${INSTALLATION_TYPE}" == "DEVELOPER" } { + expect "Configuring redis access..." + send "\025$DS_REDIS_HOST\r" + } + + expect "Configuring AMQP access... " + expect -re "Host" + send "\025$DS_RABBITMQ_HOST\r" + + expect -re "User" + send "\025$DS_RABBITMQ_USER\r" + + expect -re "Password" + send "\025$DS_RABBITMQ_PWD\r" + + expect eof + +EOF + DOCUMENT_SERVER_INSTALLED="true"; +fi + +NGINX_ROOT_DIR="/etc/nginx" + +NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-$(grep processor /proc/cpuinfo | wc -l)}; +NGINX_WORKER_CONNECTIONS=${NGINX_WORKER_CONNECTIONS:-$(ulimit -n)}; + +sed 's/^worker_processes.*/'"worker_processes ${NGINX_WORKER_PROCESSES};"'/' -i ${NGINX_ROOT_DIR}/nginx.conf +sed 's/worker_connections.*/'"worker_connections ${NGINX_WORKER_CONNECTIONS};"'/' -i ${NGINX_ROOT_DIR}/nginx.conf + +if rpm -q "firewalld"; then + firewall-cmd --permanent --zone=public --add-service=http + firewall-cmd --permanent --zone=public --add-service=https + systemctl restart firewalld.service +fi + +if [ "$APPSERVER_INSTALLED" = "false" ]; then + + ${package_manager} install -y ${package_sysname}-appserver.x86_64 + + if [ "${MYSQL_FIRST_TIME_INSTALL}" = "true" ]; then +expect << EOF + set timeout -1 + log_user 1 + + spawn appserver-configuration.sh + expect -re "Database host:" + send "\025$MYSQL_SERVER_HOST\r" + + expect -re "Database name:" + send "\025$MYSQL_SERVER_DB_NAME\r" + + expect -re "Database user:" + send "\025$MYSQL_SERVER_USER\r" + + expect -re "Database password:" + send "\025$MYSQL_TEMPORARY_ROOT_PASS\r" + + expect eof +EOF + APPSERVER_INSTALLED="true"; + else + bash appserver-configuration.sh + fi +fi + +echo "" +echo "$RES_INSTALL_SUCCESS" +echo "$RES_QUESTIONS" +echo "" diff --git a/build/install/OneClickInstall/install-RedHat/install-preq.sh b/build/install/OneClickInstall/install-RedHat/install-preq.sh new file mode 100644 index 0000000000..6af7961ed7 --- /dev/null +++ b/build/install/OneClickInstall/install-RedHat/install-preq.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +set -e + +cat< /etc/yum.repos.d/elasticsearch.repo </dev/null || useradd -m -d /var/www/appserver/services/kafka -s /sbin/nologin -p kafka kafka +cd /var/www/appserver/services/kafka +wget https://downloads.apache.org/kafka/2.7.0/kafka_2.13-2.7.0.tgz +tar xzf kafka_*.tgz --strip 1 && rm -rf kafka_*.tgz +chown -R kafka /var/www/appserver/services/kafka +cd - + +cat > /etc/systemd/system/zookeeper.service < /var/www/appserver/services/kafka/zookeeper.log 2>&1' +ExecStop=/var/www/appserver/services/kafka/bin/zookeeper-server-stop.sh +Restart=on-abnormal +[Install] +WantedBy=multi-user.target +END + +cat > /etc/systemd/system/kafka.service < /var/www/appserver/services/kafka/kafka.log 2>&1' +ExecStop=/var/www/appserver/services/kafka/bin/kafka-server-stop.sh +Restart=on-abnormal +[Install] +WantedBy=multi-user.target +END + +# add nginx repo +cat > /etc/yum.repos.d/nginx.repo < /etc/yum.repos.d/rabbitmq-server.repo < +ExclusiveArch: x86_64 +AutoReq: no +AutoProv: no +License: GPLv3 +Source0: https://github.com/ONLYOFFICE/appserver/archive/%GIT_BRANCH.tar.gz +BuildRequires: nodejs >= 12.0 +BuildRequires: yarn +BuildRequires: libgdiplus +BuildRequires: dotnet-sdk-3.1 +Requires: %name-backup +Requires: %name-files_services +Requires: %name-notify +Requires: %name-files +Requires: %name-api_system +Requires: %name-proxy +Requires: %name-people.server +Requires: %name-urlshortener +Requires: %name-thumbnails +Requires: %name-studio +Requires: %name-studio.notify +Requires: %name-socket +Requires: %name-api +AutoReqProv: no +%description +App Server is a platform for building your own online office by connecting ONLYOFFICE modules packed as separate apps. + +%include package.spec + +%prep + +rm -rf %{_rpmdir}/%{_arch}/%{name}-* +%setup -n AppServer-%GIT_BRANCH + +%include build.spec + +%include install.spec + +%include files.spec + +%pre + +%pre common + +getent group onlyoffice >/dev/null || groupadd -r onlyoffice +getent passwd onlyoffice >/dev/null || useradd -r -g onlyoffice -s /sbin/nologin onlyoffice + +%post + +chmod +x %{_bindir}/appserver-configuration.sh + +%preun + +%postun + +%clean + +rm -rf %{buildroot} + +%changelog diff --git a/build/install/rpm/SPECS/build.spec b/build/install/rpm/SPECS/build.spec new file mode 100644 index 0000000000..48e02766ac --- /dev/null +++ b/build/install/rpm/SPECS/build.spec @@ -0,0 +1,99 @@ +%build + +cd %{_builddir}/AppServer-%GIT_BRANCH/build/install/common/systemd/ +bash build.sh + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +yarn install --cwd web/ASC.Web.Components --frozen-lockfile > build/ASC.Web.Components.log +yarn pack --cwd web/ASC.Web.Components + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) +yarn remove asc-web-components --cwd web/ASC.Web.Common --peer +yarn add file:../../$component --cwd web/ASC.Web.Common --cache-folder ../../yarn --peer +yarn install --cwd web/ASC.Web.Common --frozen-lockfile > build/ASC.Web.Common.log +yarn pack --cwd web/ASC.Web.Common + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +npm run-script build:storybook --prefix web/ASC.Web.Components + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) +common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) +yarn remove asc-web-components asc-web-common --cwd web/ASC.Web.Client +yarn add ../../$component --cwd web/ASC.Web.Client --cache-folder ../../yarn +yarn add ../../$common --cwd web/ASC.Web.Client --cache-folder ../../yarn +yarn install --cwd web/ASC.Web.Client --frozen-lockfile || (cd web/ASC.Web.Client \ +npm i && cd ../../) +npm run-script build --prefix web/ASC.Web.Client + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) +common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) +yarn remove asc-web-components asc-web-common --cwd products/ASC.Files/Client +yarn add ../../../$component --cwd products/ASC.Files/Client --cache-folder ../../../yarn +yarn add ../../../$common --cwd products/ASC.Files/Client --cache-folder ../../../yarn +yarn install --cwd products/ASC.Files/Client --frozen-lockfile || (cd products/ASC.Files/Client \ +npm i && cd ../../../) +npm run-script build --prefix products/ASC.Files/Client + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +component=$(ls web/ASC.Web.Components/asc-web-components-v1.*.tgz) +common=$(ls web/ASC.Web.Common/asc-web-common-v1.*.tgz) +yarn remove asc-web-components asc-web-common --cwd products/ASC.People/Client +yarn add ../../../$component --cwd products/ASC.People/Client --cache-folder ../../../yarn +yarn add ../../../$common --cwd products/ASC.People/Client --cache-folder ../../../yarn +yarn install --cwd products/ASC.People/Client --frozen-lockfile || (cd products/ASC.People/Client \ +npm i && cd ../../../) +npm run-script build --prefix products/ASC.People/Client + +cd %{_builddir}/AppServer-%GIT_BRANCH/ +dotnet restore ASC.Web.sln --configfile .nuget/NuGet.Config +dotnet build -r linux-x64 ASC.Web.sln +cd products/ASC.People/Server +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/products/ASC.People/server +cd ../../../ +cd products/ASC.Files/Server +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/products/ASC.Files/server +cp -avrf DocStore %{_builddir}%{_var}/www/appserver/products/ASC.Files/server/ +cd ../../../ +cd products/ASC.Files/Service +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/products/ASC.Files/service +cd ../../../ +cd web/ASC.Web.Api +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/studio/api +cd ../../ +cd web/ASC.Web.Studio +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/studio/server +cd ../../ +cd common/services/ASC.Data.Backup +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/services/backup +cd ../../../ +cd common/services/ASC.Notify +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/services/notify +cd ../../../ +cd common/services/ASC.ApiSystem +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/services/apisystem +cd ../../../ +cd common/services/ASC.Thumbnails.Svc +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}/services/thumb/service +cd ../../../ + +yarn install --cwd common/ASC.Thumbnails --frozen-lockfile + +cd common/services/ASC.UrlShortener.Svc +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}/services/urlshortener/service +cd ../../../ +yarn install --cwd common/ASC.UrlShortener --frozen-lockfile + +cd common/services/ASC.Socket.IO.Svc +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}/services/socket/service +cd ../../../ +yarn install --cwd common/ASC.Socket.IO --frozen-lockfile + +cd common/services/ASC.Studio.Notify +dotnet add ASC.Studio.Notify.csproj reference ../../../products/ASC.People/Server/ASC.People.csproj ../../../products/ASC.Files/Server/ASC.Files.csproj +dotnet -d publish --no-build --self-contained -r linux-x64 -o %{_builddir}%{_var}/www/appserver/services/studio.notify +cd ../../../ + +sed -i "s@var/www@var/www/appserver@" config/nginx/onlyoffice-*.conf diff --git a/build/install/rpm/SPECS/files.spec b/build/install/rpm/SPECS/files.spec new file mode 100644 index 0000000000..053c0315f1 --- /dev/null +++ b/build/install/rpm/SPECS/files.spec @@ -0,0 +1,144 @@ +%files +%config %attr(644, root, root) %{_bindir}/* + +%files api +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/studio/api/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-api.service +%dir %{_var}/www/appserver/studio +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/products/ASC.Files/server/ + +%files backup +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/services/backup/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-backup.service +%dir %{_var}/www/appserver/services/ +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/products/ASC.Files/server/ + +%files common +%defattr(-, onlyoffice, onlyoffice, -) +%config %{_sysconfdir}/onlyoffice/appserver/ +%{_var}/log/onlyoffice/appserver/ +%{_var}/www/appserver/sql/ +%dir %{_sysconfdir}/onlyoffice/ +%dir %{_var}/log/onlyoffice/ + +%files files_services +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/products/ASC.Files/service/ +%{_sysconfdir}/systemd/system/appserver-files_service.service +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.Files/ + +%files notify +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/services/notify/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-notify.service +%dir %{_var}/www/appserver/services/ +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/products/ASC.Files/server/ + +%files files +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/products/ASC.Files/server/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_sysconfdir}/systemd/system/appserver-files.service +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ + +%files api_system +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/services/apisystem/ +%{_sysconfdir}/systemd/system/appserver-api_system.service +%dir %{_var}/www/appserver/services/ + +%files proxy +%defattr(-, onlyoffice, onlyoffice, -) +%{_sysconfdir}/nginx/includes/* +%{_sysconfdir}/nginx/conf.d/* +%{_var}/www/appserver/story/ +%{_var}/www/appserver/products/ASC.People/client/ +%{_var}/www/appserver/products/ASC.Files/client/ +%{_var}/www/appserver/public/ +%{_var}/www/appserver/studio/client/ +%dir %{_var}/www/appserver/studio/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.Files/ + +%files studio.notify +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/services/studio.notify/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-studio_notify.service +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/services/ + +%files people.server +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/products/ASC.People/server/ +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-people.service +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/products/ASC.Files/server/ + +%files urlshortener +%defattr(-, onlyoffice, onlyoffice, -) +/services/ASC.UrlShortener/ +%{_sysconfdir}/systemd/system/appserver-urlshortener.service +%dir /services/ +%dir /services/ASC.UrlShortener/ +%dir /services/ASC.UrlShortener/service + +%files thumbnails +%defattr(-, onlyoffice, onlyoffice, -) +/services/ASC.Thumbnails/ +%{_sysconfdir}/systemd/system/appserver-thumbnails.service +%dir /services/ +%dir /services/ASC.Thumbnails/ +%dir /services/ASC.Thumbnails/service + +%files socket +%defattr(-, onlyoffice, onlyoffice, -) +/services/ASC.Socket.IO/ +%{_sysconfdir}/systemd/system/appserver-socket.service +%dir /services/ +%dir /services/ASC.Socket.IO/ +%dir /services/ASC.Socket.IO/service + +%files studio +%defattr(-, onlyoffice, onlyoffice, -) +%{_var}/www/appserver/studio/server/ +%{_var}/www/appserver/products/ASC.People/server/ASC.People.dll +%{_var}/www/appserver/products/ASC.Files/server/ASC.Files*.dll +%{_sysconfdir}/systemd/system/appserver-studio.service +%dir %{_var}/www/appserver/studio/ +%dir %{_var}/www/appserver/products/ +%dir %{_var}/www/appserver/products/ASC.People/ +%dir %{_var}/www/appserver/products/ASC.People/server/ +%dir %{_var}/www/appserver/products/ASC.Files/ +%dir %{_var}/www/appserver/products/ASC.Files/server/ diff --git a/build/install/rpm/SPECS/install.spec b/build/install/rpm/SPECS/install.spec new file mode 100644 index 0000000000..97aec6ad88 --- /dev/null +++ b/build/install/rpm/SPECS/install.spec @@ -0,0 +1,53 @@ +%install +mkdir -p "%{buildroot}%{_bindir}/" +mkdir -p "%{buildroot}%{_sysconfdir}/nginx/conf.d/" +mkdir -p "%{buildroot}%{_sysconfdir}/nginx/includes" +mkdir -p "%{buildroot}%{_sysconfdir}/onlyoffice/appserver/" +mkdir -p "%{buildroot}%{_sysconfdir}/onlyoffice/appserver/data" +mkdir -p "%{buildroot}%{_sysconfdir}/onlyoffice/appserver/.private/" +mkdir -p "%{buildroot}%{_sysconfdir}/systemd/system/" +mkdir -p "%{buildroot}%{_var}/log/onlyoffice/appserver/" +mkdir -p "%{buildroot}%{_var}/www/appserver/products/ASC.Files/client/" +mkdir -p "%{buildroot}%{_var}/www/appserver/products/ASC.Files/server/" +mkdir -p "%{buildroot}%{_var}/www/appserver/products/ASC.Files/service/" +mkdir -p "%{buildroot}%{_var}/www/appserver/products/ASC.People/client/" +mkdir -p "%{buildroot}%{_var}/www/appserver/products/ASC.People/server/" +mkdir -p "%{buildroot}%{_var}/www/appserver/public/" +mkdir -p "%{buildroot}%{_var}/www/appserver/services/apisystem/" +mkdir -p "%{buildroot}%{_var}/www/appserver/services/backup/" +mkdir -p "%{buildroot}%{_var}/www/appserver/services/notify/" +mkdir -p "%{buildroot}%{_var}/www/appserver/services/studio.notify/" +mkdir -p "%{buildroot}%{_var}/www/appserver/sql/" +mkdir -p "%{buildroot}%{_var}/www/appserver/story/" +mkdir -p "%{buildroot}%{_var}/www/appserver/studio/api/" +mkdir -p "%{buildroot}%{_var}/www/appserver/studio/client/" +mkdir -p "%{buildroot}%{_var}/www/appserver/studio/server/" +mkdir -p "%{buildroot}/services/ASC.Socket.IO/service" +mkdir -p "%{buildroot}/services/ASC.Thumbnails/service/" +mkdir -p "%{buildroot}/services/ASC.UrlShortener/service/" +cp -rf %{_builddir}%{_var}/www/appserver/products/ASC.Files/server/* "%{buildroot}%{_var}/www/appserver/products/ASC.Files/server/" +cp -rf %{_builddir}%{_var}/www/appserver/products/ASC.Files/service/* "%{buildroot}%{_var}/www/appserver/products/ASC.Files/service/" +cp -rf %{_builddir}%{_var}/www/appserver/products/ASC.People/server/* "%{buildroot}%{_var}/www/appserver/products/ASC.People/server/" +cp -rf %{_builddir}%{_var}/www/appserver/services/apisystem/* "%{buildroot}%{_var}/www/appserver/services/apisystem/" +cp -rf %{_builddir}%{_var}/www/appserver/services/backup/* "%{buildroot}%{_var}/www/appserver/services/backup/" +cp -rf %{_builddir}%{_var}/www/appserver/services/notify/* "%{buildroot}%{_var}/www/appserver/services/notify/" +cp -rf %{_builddir}%{_var}/www/appserver/services/studio.notify/* "%{buildroot}%{_var}/www/appserver/services/studio.notify/" +cp -rf %{_builddir}%{_var}/www/appserver/studio/api/* "%{buildroot}%{_var}/www/appserver/studio/api" +cp -rf %{_builddir}%{_var}/www/appserver/studio/server/* "%{buildroot}%{_var}/www/appserver/studio/server/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/build/install/docker/config/*.sql "%{buildroot}%{_var}/www/appserver/sql" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/build/install/rpm/*.sh "%{buildroot}%{_bindir}/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/build/install/common/systemd/modules/* "%{buildroot}%{_sysconfdir}/systemd/system" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/common/ASC.Socket.IO/* "%{buildroot}/services/ASC.Socket.IO" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/common/ASC.Thumbnails/* "%{buildroot}/services/ASC.Thumbnails" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/common/ASC.UrlShortener/* "%{buildroot}/services/ASC.UrlShortener" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/config/* "%{buildroot}%{_sysconfdir}/onlyoffice/appserver/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/config/nginx/includes/onlyoffice*.conf "%{buildroot}%{_sysconfdir}/nginx/includes/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/config/nginx/onlyoffice*.conf "%{buildroot}%{_sysconfdir}/nginx/conf.d/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/products/ASC.Files/Client/build/* "%{buildroot}%{_var}/www/appserver/products/ASC.Files/client" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/products/ASC.People/Client/build/* "%{buildroot}%{_var}/www/appserver/products/ASC.People/client" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/public/* "%{buildroot}%{_var}/www/appserver/public/" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/web/ASC.Web.Client/build/* "%{buildroot}%{_var}/www/appserver/studio/client" +cp -rf %{_builddir}/AppServer-%GIT_BRANCH/web/ASC.Web.Components/storybook-static/* "%{buildroot}%{_var}/www/appserver/story/" +cp -rf %{_builddir}/services/socket/service/* "%{buildroot}/services/ASC.Socket.IO/service/" +cp -rf %{_builddir}/services/thumb/service/* "%{buildroot}/services/ASC.Thumbnails/service/" +cp -rf %{_builddir}/services/urlshortener/service/* "%{buildroot}/services/ASC.UrlShortener/service/" diff --git a/build/install/rpm/SPECS/package.spec b/build/install/rpm/SPECS/package.spec new file mode 100644 index 0000000000..3282808f91 --- /dev/null +++ b/build/install/rpm/SPECS/package.spec @@ -0,0 +1,112 @@ +%package backup +Summary: backup +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description backup + +%package common +Summary: common +Group: Applications/Internet +%description common + +%package files_services +Summary: files_services +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description files_services + +%package notify +Summary: notify +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description notify + +%package files +Summary: files +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description files + +%package api_system +Summary: api_system +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description api_system + +%package proxy +Summary: proxy +Group: Applications/Internet +Requires: %name-common +Requires: nginx >= 1.9.5 +Requires: mysql-community-client >= 5.7.0 +AutoReqProv: no +%description proxy + +%package studio.notify +Summary: studio.notify +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description studio.notify + +%package people.server +Summary: people.server +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description people.server + +%package urlshortener +Summary: urlshortener +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +Requires: nodejs >= 12.0 +AutoReqProv: no +%description urlshortener + +%package socket +Summary: socket +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +Requires: nodejs >= 12.0 +AutoReqProv: no +%description socket + +%package thumbnails +Summary: thumbnails +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +Requires: nodejs >= 12.0 +AutoReqProv: no +%description thumbnails + +%package studio +Summary: studio +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description studio + +%package api +Summary: api +Group: Applications/Internet +Requires: %name-common +Requires: dotnet-sdk-3.1 +AutoReqProv: no +%description api diff --git a/build/install/rpm/appserver-configuration.sh b/build/install/rpm/appserver-configuration.sh new file mode 100644 index 0000000000..73b7ffa535 --- /dev/null +++ b/build/install/rpm/appserver-configuration.sh @@ -0,0 +1,547 @@ +#!/bin/bash +ENVIRONMENT="production" + +APP_DIR="/etc/onlyoffice/appserver" +APP_CONF="$APP_DIR/appsettings.json" +USER_CONF="$APP_DIR/appsettings.$ENVIRONMENT.json" +NGINX_CONF="/etc/nginx/conf.d" +SYSTEMD_DIR="/etc/systemd/system" + +MYSQL="" +DB_HOST="" +DB_PORT="3306" +DB_NAME="" +DB_USER="" +DB_PWD="" + +APP_HOST="localhost" +APP_PORT="80" + +DOCUMENT_SERVER_HOST="localhost"; +DOCUMENT_SERVER_PORT="8083"; + +KAFKA_HOST="localhost" +KAFKA_PORT="9092" +ZOOKEEPER_HOST="localhost" +ZOOKEEPER_PORT="2181" + +ELK_SHEME="http" +ELK_HOST="localhost" +ELK_PORT="9200" + +JSON="json -I -f" +JSON_USERCONF="$JSON $USER_CONF -e" +JSON_DSCONF="$JSON $DS_CONF -e" + +[ $(id -u) -ne 0 ] && { echo "Root privileges required"; exit 1; } + +while [ "$1" != "" ]; do + case $1 in + + -ash | --appshost ) + if [ "$2" != "" ]; then + APP_HOST=$2 + shift + fi + ;; + + -asp | --appsport ) + if [ "$2" != "" ]; then + APP_PORT=$2 + shift + fi + ;; + + -dsh | --docshost ) + if [ "$2" != "" ]; then + DOCUMENT_SERVER_HOST=$2 + shift + fi + ;; + + -dsp | --docsport ) + if [ "$2" != "" ]; then + DOCUMENT_SERVER_PORT=$2 + shift + fi + ;; + + -kh | --kafkahost ) + if [ "$2" != "" ]; then + KAFKA_HOST=$2 + shift + fi + ;; + + -kp | --kafkaport ) + if [ "$2" != "" ]; then + KAFKA_PORT=$2 + shift + fi + ;; + + -zkh | --zookeeperhost ) + if [ "$2" != "" ]; then + ZOOKEEPER_HOST=$2 + shift + fi + ;; + + -zkp | --zookeeperport ) + if [ "$2" != "" ]; then + ZOOKEEPER_HOST=$2 + shift + fi + ;; + + -esh | --elastichost ) + if [ "$2" != "" ]; then + ELK_HOST=$2 + shift + fi + ;; + + -esp | --elasticport ) + if [ "$2" != "" ]; then + ELK_HOST=$2 + shift + fi + ;; + + -e | --environment ) + if [ "$2" != "" ]; then + ENVIRONMENT=$2 + shift + fi + ;; + + -? | -h | --help ) + echo " Usage: bash appserver-configuration.sh [PARAMETER] [[PARAMETER], ...]" + echo + echo " Parameters:" + echo " -ash, --appshost appserver ip" + echo " -asp, --appsport appserver port (default 80)" + echo " -dsh, --docshost document server ip" + echo " -dsp, --docsport document server port (default 8083)" + echo " -kh, --kafkahost kafka ip" + echo " -kp, --kafkaport kafka port (default 9092)" + echo " -zkh, --zookeeperhost zookeeper ip" + echo " -zkp, --zookeeperport zookeeper port (default 2181)" + echo " -esh, --elastichost elasticsearch ip" + echo " -esp, --elasticport elasticsearch port (default 9200)" + echo " -e, --environment environment (default 'production')" + echo " -?, -h, --help this help" + echo + exit 0 + ;; + + * ) + echo "Unknown parameter $1" 1>&2 + exit 1 + ;; + esac + shift +done + +set_core_machinekey () { + if [ -f $APP_DIR/.private/machinekey ]; then + CORE_MACHINEKEY=$(cat $APP_DIR/.private/machinekey) + else + CORE_MACHINEKEY=$(cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 12); + echo $CORE_MACHINEKEY >> $APP_DIR/.private/machinekey + fi +} + +install_json() { + + if [ ! -e /usr/bin/json ]; then + echo -n "Install json package... " + npm i json -g >/dev/null 2>&1 + echo "OK" + fi + + #Creating a user-defined .json + if [ ! -e $USER_CONF ]; then + echo "{}" >> $USER_CONF + chown onlyoffice:onlyoffice $USER_CONF + + set_core_machinekey + $JSON_USERCONF "this.core={'base-domain': \"$APP_HOST\", 'machinekey': \"$CORE_MACHINEKEY\"}" >/dev/null 2>&1 + $JSON $APP_CONF -e "this.core.products.subfolder='server'" >/dev/null 2>&1 #Fix error + fi +} + +restart_services() { + echo -n "Restarting services... " + + for SVC in nginx mysqld appserver-api appserver-socket appserver-api_system appserver-backup \ + appserver-files appserver-files_service appserver-notify appserver-people appserver-studio appserver-studio_notify \ + appserver-thumbnails appserver-urlshortener elasticsearch kafka zookeeper + do + sed -i "s/ENVIRONMENT=.*/ENVIRONMENT=$ENVIRONMENT/" $SYSTEMD_DIR/$SVC.service >/dev/null 2>&1 + + if systemctl is-active $SVC | grep -q "active"; then + systemctl restart $SVC.service >/dev/null 2>&1 + else + systemctl enable $SVC.service >/dev/null 2>&1 + systemctl start $SVC.service >/dev/null 2>&1 + fi + if systemctl is-active $SVC | grep -v "active" >/dev/null; then + echo -e "\033[31m $SVC not started \033[0m" + fi + done + echo "OK" +} + +input_db_params(){ + local user_connectionString=$(json -f $USER_CONF ConnectionStrings.default.connectionString) + local def_DB_HOST=$(echo $user_connectionString | grep -oP 'Server=\K.*' | grep -o '^[^;]*') + local def_DB_NAME=$(echo $user_connectionString | grep -oP 'Database=\K.*' | grep -o '^[^;]*') + local def_DB_USER=$(echo $user_connectionString | grep -oP 'User ID=\K.*' | grep -o '^[^;]*') + + read -e -p "Database host: " -i "$DB_HOST" DB_HOST + read -e -p "Database name: " -i "$DB_NAME" DB_NAME + read -e -p "Database user: " -i "$DB_USER" DB_USER + read -e -p "Database password: " -s DB_PWD + + if [ -z $DB_HOST ]; then + DB_HOST="${def_DB_HOST}"; + fi + + if [ -z $DB_NAME ]; then + DB_NAME="${def_DB_NAME}"; + fi + + if [ -z $DB_USER ]; then + DB_USER="${def_DB_USER}"; + fi + + echo +} + +establish_mysql_conn(){ + echo -n "Trying to establish MySQL connection... " + + command -v mysql >/dev/null 2>&1 || { echo "MySQL client not found"; exit 1; } + + MYSQL="mysql -h$DB_HOST -u$DB_USER" + if [ -n "$DB_PWD" ]; then + MYSQL="$MYSQL -p$DB_PWD" + fi + + $MYSQL -e ";" >/dev/null 2>&1 + ERRCODE=$? + if [ $ERRCODE -ne 0 ]; then + systemctl mysqld start >/dev/null 2>&1 + $MYSQL -e ";" >/dev/null 2>&1 || { echo "FAILURE"; exit 1; } + fi + + #Save db settings in .json + $JSON_USERCONF "this.ConnectionStrings={'default': {'connectionString': \ + \"Server=$DB_HOST;Port=$DB_PORT;Database=$DB_NAME;User ID=$DB_USER;Password=$DB_PWD;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none\"}}" >/dev/null 2>&1 + + echo "OK" +} + +mysql_check_connection() { + while ! $MYSQL -e ";" >/dev/null 2>&1; do + sleep 1 + done +} + +change_mysql_config(){ + local CNF_PATH="/etc/my.cnf"; + local CNF_SERVICE_PATH="/usr/lib/systemd/system/mysqld.service"; + + if ! grep -q "\[mysqld\]" ${CNF_PATH}; then + CNF_PATH="/etc/my.cnf.d/server.cnf"; + + if ! grep -q "\[mysqld\]" ${CNF_PATH}; then + exit 1; + fi + fi + + if ! grep -q "\[Unit\]" ${CNF_SERVICE_PATH}; then + CNF_SERVICE_PATH="/lib/systemd/system/mysqld.service"; + + if ! grep -q "\[Unit\]" ${CNF_SERVICE_PATH}; then + CNF_SERVICE_PATH="/lib/systemd/system/mariadb.service"; + + if ! grep -q "\[Unit\]" ${CNF_SERVICE_PATH}; then + exit 1; + fi + fi + fi + + sed '/skip-networking/d' -i ${CNF_PATH} || true # ignore errors + + if ! grep -q "^sql_mode" ${CNF_PATH}; then + sed "/\[mysqld\]/a sql_mode = 'NO_ENGINE_SUBSTITUTION'" -i ${CNF_PATH} # disable new STRICT mode in mysql 5.7 + else + sed "s/sql_mode.*/sql_mode = 'NO_ENGINE_SUBSTITUTION'/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^max_connections" ${CNF_PATH}; then + sed '/\[mysqld\]/a max_connections = 1000' -i ${CNF_PATH} + else + sed "s/max_connections.*/max_connections = 1000/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^group_concat_max_len" ${CNF_PATH}; then + sed '/\[mysqld\]/a group_concat_max_len = 2048' -i ${CNF_PATH} + else + sed "s/group_concat_max_len.*/group_concat_max_len = 2048/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^max_allowed_packet" ${CNF_PATH}; then + sed '/\[mysqld\]/a max_allowed_packet = 1048576000' -i ${CNF_PATH} + else + sed "s/max_allowed_packet.*/max_allowed_packet = 1048576000/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^character_set_server" ${CNF_PATH}; then + sed '/\[mysqld\]/a character_set_server = utf8' -i ${CNF_PATH} + else + sed "s/character_set_server.*/character_set_server = utf8/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^collation_server" ${CNF_PATH}; then + sed '/\[mysqld\]/a collation_server = utf8_general_ci' -i ${CNF_PATH} + else + sed "s/collation_server.*/collation_server = utf8_general_ci/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^default-authentication-plugin" ${CNF_PATH}; then + sed '/\[mysqld\]/a default-authentication-plugin = mysql_native_password' -i ${CNF_PATH} + else + sed "s/default-authentication-plugin.*/default-authentication-plugin = mysql_native_password/" -i ${CNF_PATH} || true # ignore errors + fi + + if ! grep -q "^LimitNOFILE" ${CNF_SERVICE_PATH}; then + sed '/\[Service\]/a LimitNOFILE = infinity' -i ${CNF_SERVICE_PATH} + else + sed "s/LimitNOFILE.*/LimitNOFILE = infinity/" -i ${CNF_SERVICE_PATH} || true # ignore errors + fi + + if ! grep -q "^LimitMEMLOCK" ${CNF_SERVICE_PATH}; then + sed '/\[Service\]/a LimitMEMLOCK = infinity' -i ${CNF_SERVICE_PATH} + else + sed "s/LimitMEMLOCK.*/LimitMEMLOCK = infinity/" -i ${CNF_SERVICE_PATH} || true # ignore errors + fi + + systemctl daemon-reload >/dev/null 2>&1 + systemctl restart mysqld >/dev/null 2>&1 +} + +execute_mysql_script(){ + + change_mysql_config + + mysql_check_connection + + if [ "$DB_USER" = "root" ] && [ ! "$(mysql -V | grep ' 5.5.')" ]; then + # allow connect via mysql_native_password with root and empty password + $MYSQL -D "mysql" -e "update user set plugin='mysql_native_password' where user='root';ALTER USER '${DB_USER}'@'localhost' IDENTIFIED WITH mysql_native_password BY '${DB_PWD}';" >/dev/null 2>&1 + fi + + #Checking the quantity of the tables created in the db + DB_TABLES_COUNT=$($MYSQL --silent --skip-column-names -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='${DB_NAME}'"); + + if [ "${DB_TABLES_COUNT}" -eq "0" ]; then + local SQL_DIR="/var/www/appserver/sql" + + echo -n "Installing MYSQL database... " + + #Adding data to the db + sed -i -e '1 s/^/SET SQL_MODE='ALLOW_INVALID_DATES';\n/;' $SQL_DIR/onlyoffice.sql + $MYSQL -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8 COLLATE 'utf8_general_ci';" >/dev/null 2>&1 + $MYSQL "$DB_NAME" < "$SQL_DIR/createdb.sql" >/dev/null 2>&1 + $MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.sql" >/dev/null 2>&1 + $MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.data.sql" >/dev/null 2>&1 + $MYSQL "$DB_NAME" < "$SQL_DIR/onlyoffice.resources.sql" >/dev/null 2>&1 + else + echo -n "Upgrading MySQL database... " + fi + echo "OK" +} + +setup_nginx(){ + echo -n "Configuring nginx... " + + mv -f $NGINX_CONF/default.conf $NGINX_CONF/default.conf.old >/dev/null 2>&1 + + sed -i "s/listen.*;/listen $APP_PORT;/" $NGINX_CONF/onlyoffice.conf + + shopt -s nocasematch + PORTS=() + if $(getenforce) >/dev/null 2>&1; then + case $(getenforce) in + enforcing|permissive) + PORTS+=('8081') #Storybook + PORTS+=("$DOCUMENT_SERVER_PORT") + PORTS+=('5001') #ASC.Web.Studio + PORTS+=('5002') #ASC.People + PORTS+=('5008') #ASC.Files + setsebool -P httpd_can_network_connect on + ;; + disabled) + : + ;; + esac + + for PORT in ${PORTS[@]}; do + semanage port -a -t http_port_t -p tcp $PORT >/dev/null 2>&1 || \ + semanage port -m -t http_port_t -p tcp $PORT >/dev/null 2>&1 || \ + true + done + fi + chown nginx:nginx /etc/nginx/* -R + sudo sed -e 's/#//' -i $NGINX_CONF/onlyoffice.conf + echo "OK" +} + +setup_docs() { + echo -n "Configuring Docs... " + local DS_CONF="/etc/onlyoffice/documentserver/local.json" + + #Changing the Docs port in nginx conf + sed -i "s/0.0.0.0:.*;/0.0.0.0:$DOCUMENT_SERVER_PORT;/" $NGINX_CONF/ds.conf + sed -i "s/]:.*;/]:$DOCUMENT_SERVER_PORT default_server;/g" $NGINX_CONF/ds.conf + sed "0,/proxy_pass .*;/{s/proxy_pass .*;/proxy_pass http:\/\/${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT};/}" -i $NGINX_CONF/onlyoffice.conf + + #Enable JWT validation for Docs + $JSON_DSCONF "this.services.CoAuthoring.token.enable.browser='true'" >/dev/null 2>&1 + $JSON_DSCONF "this.services.CoAuthoring.token.enable.request.inbox='true'" >/dev/null 2>&1 + $JSON_DSCONF "this.services.CoAuthoring.token.enable.request.outbox='true'" >/dev/null 2>&1 + + local DOCUMENT_SERVER_JWT_SECRET=$(cat ${DS_CONF} | json services.CoAuthoring.secret.inbox.string) + local DOCUMENT_SERVER_JWT_HEADER=$(cat ${DS_CONF} | json services.CoAuthoring.token.inbox.header) + + #Save Docs address and JWT in .json + $JSON_USERCONF "this.files={'docservice': {\ + 'secret': {'value': \"$DOCUMENT_SERVER_JWT_SECRET\",'header': \"$DOCUMENT_SERVER_JWT_HEADER\"}, \ + 'url': {'public': '/ds-vpath/','internal': \"http://${DOCUMENT_SERVER_HOST}:${DOCUMENT_SERVER_PORT}\",'portal': \"http://$APP_HOST:$APP_PORT\"}}}" >/dev/null 2>&1 + + #Enable ds-example autostart + sudo sed 's,autostart=false,autostart=true,' -i /etc/supervisord.d/ds-example.ini + sudo supervisorctl start ds:example >/dev/null 2>&1 + + echo "OK" +} + +change_elasticsearch_config(){ + local ELASTIC_SEARCH_VERSION=$(rpm -qi elasticsearch | grep Version | tail -n1 | awk -F': ' '/Version/ {print $2}'); + local ELASTIC_SEARCH_CONF_PATH="/etc/elasticsearch/elasticsearch.yml" + local ELASTIC_SEARCH_JAVA_CONF_PATH="/etc/elasticsearch/jvm.options"; + + if /usr/share/elasticsearch/bin/elasticsearch-plugin list | grep -q "ingest-attachment"; then + /usr/share/elasticsearch/bin/elasticsearch-plugin remove -s ingest-attachment + fi + /usr/share/elasticsearch/bin/elasticsearch-plugin install -s -b ingest-attachment + + if [ -f ${ELASTIC_SEARCH_CONF_PATH}.rpmnew ]; then + cp -rf ${ELASTIC_SEARCH_CONF_PATH}.rpmnew ${ELASTIC_SEARCH_CONF_PATH}; + fi + + if [ -f ${ELASTIC_SEARCH_JAVA_CONF_PATH}.rpmnew ]; then + cp -rf ${ELASTIC_SEARCH_JAVA_CONF_PATH}.rpmnew ${ELASTIC_SEARCH_JAVA_CONF_PATH}; + fi + + if ! grep -q "indices.fielddata.cache.size" ${ELASTIC_SEARCH_CONF_PATH}; then + echo "indices.fielddata.cache.size: 30%" >> ${ELASTIC_SEARCH_CONF_PATH} + else + sed -i "s/indices.fielddata.cache.size.*/indices.fielddata.cache.size: 30%/" ${ELASTIC_SEARCH_CONF_PATH} + fi + + if ! grep -q "indices.memory.index_buffer_size" ${ELASTIC_SEARCH_CONF_PATH}; then + echo "indices.memory.index_buffer_size: 30%" >> ${ELASTIC_SEARCH_CONF_PATH} + else + sed -i "s/indices.memory.index_buffer_size.*/indices.memory.index_buffer_size: 30%/" ${ELASTIC_SEARCH_CONF_PATH} + fi + + if grep -q "HeapDumpOnOutOfMemoryError" ${ELASTIC_SEARCH_JAVA_CONF_PATH}; then + sed "/-XX:+HeapDumpOnOutOfMemoryError/d" -i ${ELASTIC_SEARCH_JAVA_CONF_PATH} + fi + + local TOTAL_MEMORY=$(free -m | grep -oP '\d+' | head -n 1); + local MEMORY_REQUIREMENTS=12228; #RAM ~4*3Gb + + if [ ${TOTAL_MEMORY} -gt ${MEMORY_REQUIREMENTS} ]; then + if ! grep -q "[-]Xms1g" ${ELASTIC_SEARCH_JAVA_CONF_PATH}; then + echo "-Xms4g" >> ${ELASTIC_SEARCH_JAVA_CONF_PATH} + else + sed -i "s/-Xms1g/-Xms4g/" ${ELASTIC_SEARCH_JAVA_CONF_PATH} + fi + + if ! grep -q "[-]Xmx1g" ${ELASTIC_SEARCH_JAVA_CONF_PATH}; then + echo "-Xmx4g" >> ${ELASTIC_SEARCH_JAVA_CONF_PATH} + else + sed -i "s/-Xmx1g/-Xmx4g/" ${ELASTIC_SEARCH_JAVA_CONF_PATH} + fi + fi + + if [ -d /etc/elasticsearch/ ]; then + chmod g+ws /etc/elasticsearch/ + fi +} + +setup_elasticsearch() { + echo -n "Configuring elasticsearch... " + + #Save elasticsearch parameters in .json + $JSON_USERCONF "this.elastic={'Scheme': \"${ELK_SHEME}\",'Host': \"${ELK_HOST}\",'Port': \"${ELK_PORT}\" }" >/dev/null 2>&1 + + change_elasticsearch_config + + echo "OK" +} + +setup_kafka() { + + local KAFKA_SERVICE=$(systemctl --type=service | grep 'kafka' | tr -d '●' | awk '{print $1;}') + + if [ $KAFKA_SERVICE ]; then + + echo -n "Configuring kafka... " + + #Change kafka config + local KAFKA_CONF="$(cat $SYSTEMD_DIR/$KAFKA_SERVICE | grep ExecStop= | cut -c 10- | rev | cut -c 26- | rev)/config" + sed -i "s/clientPort=.*/clientPort=${ZOOKEEPER_PORT}/g" $KAFKA_CONF/zookeeper.properties + sed -i "s/zookeeper.connect=.*/zookeeper.connect=${ZOOKEEPER_HOST}:${ZOOKEEPER_PORT}/g" $KAFKA_CONF/server.properties + sed -i "s/bootstrap.servers=.*/bootstrap.servers=${KAFKA_HOST}:${KAFKA_PORT}/g" $KAFKA_CONF/consumer.properties + sed -i "s/bootstrap.servers=.*/bootstrap.servers=${KAFKA_HOST}:${KAFKA_PORT}/g" $KAFKA_CONF/connect-standalone.properties + sed -i "s/logger.kafka.controller=.*,/logger.kafka.controller=INFO,/g" $KAFKA_CONF/log4j.properties + sed -i "s/logger.state.change.logger=.*,/logger.state.change.logger=INFO,/g" $KAFKA_CONF/log4j.properties + echo "log4j.logger.kafka.producer.async.DefaultEventHandler=INFO, kafkaAppender" >> $KAFKA_CONF/log4j.properties + + #Save kafka parameters in .json + $JSON_USERCONF "this.kafka={'BootstrapServers': \"${KAFKA_HOST}:${KAFKA_PORT}\"}" >/dev/null 2>&1 + + echo "OK" + fi + +} + +install_json + +if rpm -q mysql-community-client >/dev/null; then + input_db_params + establish_mysql_conn || exit $? + execute_mysql_script || exit $? +fi + +if rpm -q nginx >/dev/null; then + setup_nginx +fi + +if rpm -q onlyoffice-documentserver >/dev/null || rpm -q onlyoffice-documentserver-de >/dev/null || rpm -q onlyoffice-documentserver-ee >/dev/null; then + setup_docs +fi + +if rpm -q elasticsearch >/dev/null; then + setup_elasticsearch +fi + +setup_kafka + +restart_services diff --git a/common/ASC.Core.Common/EF/Model/Tenant/DbTariff.cs b/common/ASC.Core.Common/EF/Model/Tenant/DbTariff.cs index 61ecf57f09..aac59c3f12 100644 --- a/common/ASC.Core.Common/EF/Model/Tenant/DbTariff.cs +++ b/common/ASC.Core.Common/EF/Model/Tenant/DbTariff.cs @@ -1,6 +1,8 @@ -using ASC.Core.Common.EF.Model; +using System; + +using ASC.Core.Common.EF.Model; + using Microsoft.EntityFrameworkCore; -using System; namespace ASC.Core.Common.EF { @@ -10,7 +12,6 @@ namespace ASC.Core.Common.EF public int Tenant { get; set; } public int Tariff { get; set; } public DateTime Stamp { get; set; } - public string TariffKey { get; set; } public string Comment { get; set; } public DateTime CreateOn { get; set; } } @@ -52,12 +53,6 @@ namespace ASC.Core.Common.EF entity.Property(e => e.Tariff).HasColumnName("tariff"); - entity.Property(e => e.TariffKey) - .HasColumnName("tariff_key") - .HasColumnType("varchar(64)") - .HasCharSet("utf8") - .HasCollation("utf8_general_ci"); - entity.Property(e => e.Tenant).HasColumnName("tenant"); }); } @@ -85,11 +80,6 @@ namespace ASC.Core.Common.EF entity.Property(e => e.Tariff).HasColumnName("tariff"); - entity.Property(e => e.TariffKey) - .HasColumnName("tariff_key") - .HasMaxLength(64) - .HasDefaultValueSql("NULL"); - entity.Property(e => e.Tenant).HasColumnName("tenant"); }); } diff --git a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.Designer.cs b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.Designer.cs index e34df495a8..521d04aeac 100644 --- a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.Designer.cs +++ b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.Designer.cs @@ -749,11 +749,6 @@ namespace ASC.Core.Common.Migrations.MySql.CoreDbContextMySql .HasColumnName("tariff") .HasColumnType("int"); - b.Property("TariffKey") - .HasColumnName("tariff_key") - .HasColumnType("varchar(64)") - .HasAnnotation("MySql:CharSet", "utf8") - .HasAnnotation("MySql:Collation", "utf8_general_ci"); b.Property("Tenant") .HasColumnName("tenant") diff --git a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.cs b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.cs index 8ba8314772..9c6727ff76 100644 --- a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.cs +++ b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/20201006100008_CoreDbContextMySql.cs @@ -105,9 +105,6 @@ namespace ASC.Core.Common.Migrations.MySql.CoreDbContextMySql tenant = table.Column(nullable: false), tariff = table.Column(nullable: false), stamp = table.Column(type: "datetime", nullable: false), - tariff_key = table.Column(type: "varchar(64)", nullable: true) - .Annotation("MySql:CharSet", "utf8") - .Annotation("MySql:Collation", "utf8_general_ci"), comment = table.Column(type: "varchar(255)", nullable: true) .Annotation("MySql:CharSet", "utf8") .Annotation("MySql:Collation", "utf8_general_ci"), diff --git a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/CoreDbContextModelSnapshot.cs b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/CoreDbContextModelSnapshot.cs index cc5fb0a011..fc70880016 100644 --- a/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/CoreDbContextModelSnapshot.cs +++ b/common/ASC.Core.Common/Migrations/MySql/CoreDbContextMySql/CoreDbContextModelSnapshot.cs @@ -747,12 +747,6 @@ namespace ASC.Core.Common.Migrations.MySql.CoreDbContextMySql .HasColumnName("tariff") .HasColumnType("int"); - b.Property("TariffKey") - .HasColumnName("tariff_key") - .HasColumnType("varchar(64)") - .HasAnnotation("MySql:CharSet", "utf8") - .HasAnnotation("MySql:Collation", "utf8_general_ci"); - b.Property("Tenant") .HasColumnName("tenant") .HasColumnType("int"); diff --git a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.Designer.cs b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.Designer.cs index ff54e3e58d..ee66bca3a0 100644 --- a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.Designer.cs +++ b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.Designer.cs @@ -736,13 +736,6 @@ namespace ASC.Core.Common.Migrations.Npgsql.CoreDbContextNpgsql .HasColumnName("tariff") .HasColumnType("integer"); - b.Property("TariffKey") - .ValueGeneratedOnAdd() - .HasColumnName("tariff_key") - .HasColumnType("character varying(64)") - .HasDefaultValueSql("NULL") - .HasMaxLength(64); - b.Property("Tenant") .HasColumnName("tenant") .HasColumnType("integer"); diff --git a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.cs b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.cs index 076347f806..74956082d5 100644 --- a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.cs +++ b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/20200929123316_CoreDbContextNpgsql.cs @@ -97,7 +97,6 @@ namespace ASC.Core.Common.Migrations.Npgsql.CoreDbContextNpgsql tenant = table.Column(nullable: false), tariff = table.Column(nullable: false), stamp = table.Column(nullable: false), - tariff_key = table.Column(maxLength: 64, nullable: true, defaultValueSql: "NULL"), comment = table.Column(maxLength: 255, nullable: true, defaultValueSql: "NULL"), create_on = table.Column(nullable: false, defaultValueSql: "CURRENT_TIMESTAMP") }, diff --git a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/CoreDbContextModelSnapshot.cs b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/CoreDbContextModelSnapshot.cs index b54f4a3045..22fcd29c5e 100644 --- a/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/CoreDbContextModelSnapshot.cs +++ b/common/ASC.Core.Common/Migrations/Npgsql/CoreDbContextNpgsql/CoreDbContextModelSnapshot.cs @@ -749,13 +749,6 @@ namespace ASC.Core.Common.Migrations.Npgsql.CoreDbContextNpgsql .HasColumnName("tariff") .HasColumnType("integer"); - b.Property("TariffKey") - .ValueGeneratedOnAdd() - .HasColumnName("tariff_key") - .HasColumnType("character varying(64)") - .HasDefaultValueSql("NULL") - .HasMaxLength(64); - b.Property("Tenant") .HasColumnName("tenant") .HasColumnType("integer"); diff --git a/packages/asc-web-common/components/PrivateRoute/index.js b/packages/asc-web-common/components/PrivateRoute/index.js index 74f82ee28f..46a1241751 100644 --- a/packages/asc-web-common/components/PrivateRoute/index.js +++ b/packages/asc-web-common/components/PrivateRoute/index.js @@ -97,7 +97,10 @@ const PrivateRoute = ({ component: Component, ...rest }) => { useEffect(() => { const currentModule = modules.find((m) => { - if (m.link.indexOf(computedMatch.path) !== -1) { + if ( + computedMatch.path !== "/" && + m.link.indexOf(computedMatch.path) !== -1 + ) { return true; } }); diff --git a/products/ASC.Files/Client/src/Files.jsx b/products/ASC.Files/Client/src/Files.jsx index b800845c40..3d76f823b8 100644 --- a/products/ASC.Files/Client/src/Files.jsx +++ b/products/ASC.Files/Client/src/Files.jsx @@ -6,7 +6,7 @@ import config from "../package.json"; import PrivateRoute from "@appserver/common/components/PrivateRoute"; import AppLoader from "@appserver/common/components/AppLoader"; import toastr from "studio/toastr"; -import { updateTempContent } from "@appserver/common/utils"; +import { combineUrl, updateTempContent } from "@appserver/common/utils"; import initFilesStore from "./store/InitFilesStore"; import filesStore from "./store/FilesStore"; import settingsStore from "./store/SettingsStore"; @@ -25,10 +25,31 @@ import { I18nextProvider } from "react-i18next"; import Home from "./components/pages/Home"; import Settings from "./components/pages/Settings"; import VersionHistory from "./components/pages/VersionHistory"; +import ErrorBoundary from "@appserver/common/components/ErrorBoundary"; import Panels from "./components/FilesPanels"; +import { AppServerConfig } from "@appserver/common/constants"; +const { proxyURL } = AppServerConfig; const homepage = config.homepage; +const PROXY_HOMEPAGE_URL = combineUrl(proxyURL, homepage); + +const HOME_URL = combineUrl(PROXY_HOMEPAGE_URL, "/"); +const SETTINGS_URL = combineUrl(PROXY_HOMEPAGE_URL, "/settings/:setting"); +const HISTORY_URL = combineUrl(PROXY_HOMEPAGE_URL, "/:fileId/history"); +const FILTER_URL = combineUrl(PROXY_HOMEPAGE_URL, "/filter"); + +if (!window.AppServer) { + window.AppServer = {}; +} + +window.AppServer.files = { + HOME_URL, + SETTINGS_URL, + HISTORY_URL, + FILTER_URL, +}; + const Error404 = React.lazy(() => import("studio/Error404")); const Error404Route = (props) => ( @@ -93,18 +114,10 @@ class FilesContent extends React.Component { <> - - - - + + + + diff --git a/products/ASC.Files/Client/src/components/Article/Body/index.js b/products/ASC.Files/Client/src/components/Article/Body/index.js index e0fe19cc99..864369c0f2 100644 --- a/products/ASC.Files/Client/src/components/Article/Body/index.js +++ b/products/ASC.Files/Client/src/components/Article/Body/index.js @@ -27,12 +27,12 @@ class ArticleBodyContent extends React.Component { }; } - componentDidMount() { + /*componentDidMount() { if (this.props.currentId) { const currentId = [this.props.currentId + ""]; this.props.setSelectedNode(currentId); } - } + }*/ onSelect = (data, e) => { const { @@ -127,7 +127,10 @@ export default inject( const { setIsLoading } = initFilesStore; const { fetchFiles, filter } = filesStore; const { treeFolders, setSelectedNode, setTreeFolders } = treeFoldersStore; - const selectedTreeNode = [selectedFolderStore.id + ""]; + const selectedTreeNode = + treeFoldersStore.selectedTreeNode.length > 0 + ? treeFoldersStore.selectedTreeNode + : [selectedFolderStore.id + ""]; return { selectedFolderTitle: selectedFolderStore.title, diff --git a/products/ASC.People/Client/src/pages/ProfileAction/Section/Body/updateUserForm.js b/products/ASC.People/Client/src/pages/ProfileAction/Section/Body/updateUserForm.js index 638ee3e834..e565dddcaa 100644 --- a/products/ASC.People/Client/src/pages/ProfileAction/Section/Body/updateUserForm.js +++ b/products/ASC.People/Client/src/pages/ProfileAction/Section/Body/updateUserForm.js @@ -711,7 +711,8 @@ class UpdateUserForm extends React.Component { buttonTabIndex={2} dataDialog={dialogsDataset.changePassword} /> - + /> */} { ); }; -const Home = (props) => { - const { defaultPage } = props; +const Home = ({ + defaultPage, + currentProductId, + setCurrentProductId, + ...props +}) => { + useEffect(() => { + console.log("SET setCurrentProductId"); + currentProductId !== "homePage" && setCurrentProductId("homePage"); + }, [currentProductId, setCurrentProductId]); + return tryRedirectTo(defaultPage) ? ( <> ) : ( @@ -117,11 +126,12 @@ Home.propTypes = { export default inject(({ auth }) => { const { isLoaded, settingsStore, moduleStore } = auth; - const { defaultPage } = settingsStore; + const { defaultPage, setCurrentProductId } = settingsStore; const { modules } = moduleStore; return { defaultPage, modules, isLoaded, + setCurrentProductId, }; })(withRouter(observer(Home))); diff --git a/web/ASC.Web.Core/Notify/StudioNotifyService.cs b/web/ASC.Web.Core/Notify/StudioNotifyService.cs index b642bf2b3e..40cc547de4 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifyService.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifyService.cs @@ -270,8 +270,7 @@ namespace ASC.Web.Studio.Core.Notify : Actions.MailboxCreated, null, StudioNotifyHelper.RecipientFromEmail(toEmails, false), - new[] { EMailSenderName }, - null); + new[] { EMailSenderName }); } public void SendMailboxPasswordChanged(List toEmails, string username, string address) @@ -281,7 +280,6 @@ namespace ASC.Web.Studio.Core.Notify null, StudioNotifyHelper.RecipientFromEmail(toEmails, false), new[] { EMailSenderName }, - null, new TagValue(Tags.UserName, username ?? string.Empty), new TagValue(Tags.Address, address ?? string.Empty)); } @@ -547,7 +545,6 @@ namespace ASC.Web.Studio.Core.Notify null, new IRecipient[] { admin }, new[] { EMailSenderName }, - null, new TagValue(Tags.FromUserName, user.DisplayUserName(DisplayUserSettingsHelper)), new TagValue(Tags.FromUserLink, GetUserProfileLink(user))); } @@ -737,7 +734,6 @@ namespace ASC.Web.Studio.Core.Notify null, new IRecipient[] { owner }, new[] { EMailSenderName }, - null, TagValues.GreenButton(greenButtonText, confirmOwnerUpdateUrl), new TagValue(Tags.UserName, newOwner.DisplayUserName(DisplayUserSettingsHelper)), new TagValue(Tags.OwnerName, owner.DisplayUserName(DisplayUserSettingsHelper))); @@ -907,7 +903,6 @@ namespace ASC.Web.Studio.Core.Notify null, new IRecipient[] { recipient }, new[] { EMailSenderName }, - null, new TagValue(Tags.UserName, u.FirstName.HtmlEncode()), new TagValue(Tags.UserLastName, u.LastName.HtmlEncode()), new TagValue(Tags.UserEmail, u.Email.HtmlEncode()), @@ -969,7 +964,6 @@ namespace ASC.Web.Studio.Core.Notify null, new[] { StudioNotifyHelper.ToRecipient(u.ID) }, new[] { EMailSenderName }, - null, new TagValue(Tags.UserName, u.FirstName.HtmlEncode()), new TagValue(Tags.PortalUrl, serverRootPath), new TagValue(Tags.ControlPanelUrl, GetControlPanelUrl(serverRootPath)));