Merge branch 'release/v1.1.0' into bugfix/file-operations
This commit is contained in:
commit
5296f8db73
161
.github/workflows/build_packages.yml
vendored
Normal file
161
.github/workflows/build_packages.yml
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
name: Build packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release/*
|
||||||
|
- develop
|
||||||
|
- hotfix/*
|
||||||
|
paths:
|
||||||
|
- build/install/deb**
|
||||||
|
- build/install/rpm**
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
BRANCH_NAME: $(echo ${GITHUB_REF#refs/heads/})
|
||||||
|
PRODUCT_LOW: $(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' )
|
||||||
|
PRODUCT: "${{ github.event.repository.name }}"
|
||||||
|
BUILD_NUMBER: "${{ github.run_number }}"
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_deb:
|
||||||
|
name: DEB packages
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Import GPG
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
|
id: gpg_step
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPG_PRIVATE_KEY_PASS }}
|
||||||
|
|
||||||
|
- name: Get files from repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Prepare build
|
||||||
|
id: get_vars
|
||||||
|
run: |
|
||||||
|
wget -O - https://dl.yarnpkg.com/debian/pubkey.gpg | \
|
||||||
|
sudo gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/yarnkey.gpg --import
|
||||||
|
sudo chmod 644 /usr/share/keyrings/yarnkey.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
wget https://packages.microsoft.com/config/$(lsb_release -is | \
|
||||||
|
tr [:upper:] [:lower:])/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||||
|
sudo dpkg -i packages-microsoft-prod.deb
|
||||||
|
wget -O - https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||||
|
sudo apt install -y dotnet-sdk-7.0 yarn nodejs dh-make rename dpkg-sig lintian
|
||||||
|
sudo npm install -g json
|
||||||
|
echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_OUTPUT
|
||||||
|
echo "VERSION=$(echo "${{ github.ref }}" | grep -oP '\d+\.\d+\.\d+' || echo "1.0.0")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd build/install/deb/
|
||||||
|
rename -f -v "s/product([^\/]*)$/${{ env.PRODUCT_LOW }}\$1/g" debian/* ../common/* ../common/logrotate/*
|
||||||
|
find ../ -type f -exec sed -i "s/{{product}}/${{ env.PRODUCT_LOW }}/g" {} ';'
|
||||||
|
sed -i "s/{{package_header_tag_version}}/${{ steps.get_vars.outputs.VERSION }}.$BUILD_NUMBER/g" \
|
||||||
|
debian/changelog debian/control
|
||||||
|
dpkg-buildpackage -uc -k${{ steps.gpg_step.outputs.fingerprint }}
|
||||||
|
|
||||||
|
- name: Upload to Nexus
|
||||||
|
run: |
|
||||||
|
for file in /home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/*.deb; do
|
||||||
|
echo $file
|
||||||
|
curl --verbose --user ${{ secrets.REPO_LOGIN }}:${{ secrets.REPO_PASS }} -H "Content-Type: multipart/form-data" \
|
||||||
|
--data-binary "@$file" ${{ secrets.REPO_URL_4TESTING_DEB }}
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: |
|
||||||
|
lintian --suppress-tags=mismatched-override --profile debian /home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/*.deb \
|
||||||
|
| tee -a file
|
||||||
|
if grep -qE '^(W:|E:)' file; then echo \
|
||||||
|
"::warning Noticedeb=lintian::$(cat file | awk '/^W:/ { ws += 1 } /^E:/ { es += 1 } END { print "Warnings:", ws, "Errors:", es }')"; fi
|
||||||
|
|
||||||
|
build_rpm:
|
||||||
|
name: RPM packages
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Import GPG
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPG_PRIVATE_KEY_PASS }}
|
||||||
|
|
||||||
|
- name: Get files from repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Prepare build
|
||||||
|
id: get_vars
|
||||||
|
run: |
|
||||||
|
wget -O - https://dl.yarnpkg.com/debian/pubkey.gpg | sudo gpg --no-default-keyring --keyring \
|
||||||
|
gnupg-ring:/usr/share/keyrings/yarnkey.gpg --import
|
||||||
|
sudo chmod 644 /usr/share/keyrings/yarnkey.gpg
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/yarn.list
|
||||||
|
wget https://packages.microsoft.com/config/$(lsb_release -is | \
|
||||||
|
tr [:upper:] [:lower:])/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
|
||||||
|
sudo dpkg -i packages-microsoft-prod.deb
|
||||||
|
wget -O - https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||||
|
sudo apt install -y dotnet-sdk-7.0 yarn nodejs dh-make rename python3-pip python3-rpm
|
||||||
|
sudo npm install -g json
|
||||||
|
sudo pip install rpmlint
|
||||||
|
echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_OUTPUT
|
||||||
|
echo "VERSION=$(echo "${ GITHUB_REF##*/ }" | grep -oP '\d+\.\d+\.\d+' || echo "1.0.0")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd build/install/rpm/SPECS
|
||||||
|
mv ./SOURCES/product.rpmlintrc ./SOURCES/${{ env.PRODUCT_LOW }}.rpmlintrc
|
||||||
|
wget https://github.com/ONLYOFFICE/${{ env.PRODUCT }}/archive/${{ env.BRANCH_NAME }}.tar.gz \
|
||||||
|
-O ./SOURCES/${{ env.PRODUCT }}-$(echo ${{ env.BRANCH_NAME }} | tr '/' '-').tar.gz
|
||||||
|
wget https://github.com/ONLYOFFICE/document-templates/archive/main/community-server.tar.gz \
|
||||||
|
-O ./SOURCES/document-templates-main-community-server.tar.gz
|
||||||
|
wget https://github.com/ONLYOFFICE/dictionaries/archive/master.tar.gz \
|
||||||
|
-O ./SOURCES/dictionaries-master.tar.gz
|
||||||
|
sed -i -e '/BuildRequires/d' product.spec
|
||||||
|
rpmbuild -D "packager Ascensio System SIA <support@onlyoffice.com>" -D "GIT_BRANCH $(echo ${{ env.BRANCH_NAME }} \
|
||||||
|
| tr '/' '-')" -D "_topdir $(pwd)" -D "version ${{ steps.get_vars.outputs.VERSION }}" \
|
||||||
|
-D "release ${{ env.BUILD_NUMBER }}" -ba product.spec
|
||||||
|
|
||||||
|
- name: Sign
|
||||||
|
run: |
|
||||||
|
cat << EOF >> $HOME/.rpmmacros
|
||||||
|
%_signature gpg
|
||||||
|
%_gpg_name ${{ secrets.GPG_KEY_NAME }}
|
||||||
|
%_gpg_path $HOME/.gnupg
|
||||||
|
%__gpg /usr/bin/gpg
|
||||||
|
EOF
|
||||||
|
gpg --export --armor --output onlyoffice-gpgkey.pub
|
||||||
|
rpm --import onlyoffice-gpgkey.pub
|
||||||
|
rpm --addsign /home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/rpm/SPECS/RPMS/x86_64/*.rpm
|
||||||
|
|
||||||
|
- name: Upload
|
||||||
|
run: |
|
||||||
|
for file in /home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/rpm/SPECS/RPMS/x86_64/*.rpm; do
|
||||||
|
curl --verbose --user ${{ secrets.REPO_LOGIN }}:${{ secrets.REPO_PASS }} \
|
||||||
|
--upload-file "$file" ${{ secrets.REPO_URL_4TESTING_RPM }}
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Rpmlint
|
||||||
|
run: |
|
||||||
|
for package in /home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/rpm/SPECS/RPMS/x86_64/*.rpm
|
||||||
|
do rpmlint --ignore-unused-rpmlintrc --rpmlintrc \
|
||||||
|
/home/runner/work/${{ env.PRODUCT }}/${{ env.PRODUCT }}/build/install/rpm/SPECS/SOURCES/${{ env.PRODUCT_LOW }}.rpmlintrc $package \
|
||||||
|
| tee -a file2
|
||||||
|
done
|
||||||
|
if grep -qE '^(W:|E:)' file2; then echo \
|
||||||
|
"::warning NoticeRpm=rpmLint::$(cat file2 | awk '/W:/ { ws += 1 } /E:/ { es += 1 } END { print "Warnings:", ws, "Errors:", es }')" ; fi
|
@ -552,14 +552,14 @@ install_docker_compose () {
|
|||||||
install_service python3
|
install_service python3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command_exists apt-get; then
|
py3_version=$(python3 -c 'import sys; print(sys.version_info.minor)')
|
||||||
apt-get -y update -qq
|
if [[ $py3_version -lt 6 ]]; then
|
||||||
apt-get -y -q install python3-pip
|
curl -O https://bootstrap.pypa.io/pip/3.$py3_version/get-pip.py
|
||||||
elif command_exists yum; then
|
else
|
||||||
curl -O https://bootstrap.pypa.io/get-pip.py
|
curl -O https://bootstrap.pypa.io/get-pip.py
|
||||||
python3 get-pip.py || true
|
fi
|
||||||
rm get-pip.py
|
python3 get-pip.py
|
||||||
fi
|
rm get-pip.py
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
python3 -m pip install docker-compose
|
python3 -m pip install docker-compose
|
||||||
@ -571,21 +571,6 @@ install_docker_compose () {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
install_jq () {
|
|
||||||
if command_exists apt-get; then
|
|
||||||
apt-get -y update
|
|
||||||
apt-get -y -q install jq
|
|
||||||
elif command_exists yum; then
|
|
||||||
rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-$REV.noarch.rpm || true
|
|
||||||
yum -y install jq
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command_exists jq; then
|
|
||||||
echo "command jq not found"
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_ports () {
|
check_ports () {
|
||||||
RESERVED_PORTS=(3306 8092);
|
RESERVED_PORTS=(3306 8092);
|
||||||
ARRAY_PORTS=();
|
ARRAY_PORTS=();
|
||||||
@ -779,10 +764,6 @@ get_available_version () {
|
|||||||
install_curl;
|
install_curl;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command_exists jq ; then
|
|
||||||
install_jq
|
|
||||||
fi
|
|
||||||
|
|
||||||
CREDENTIALS="";
|
CREDENTIALS="";
|
||||||
AUTH_HEADER="";
|
AUTH_HEADER="";
|
||||||
TAGS_RESP="";
|
TAGS_RESP="";
|
||||||
@ -953,6 +934,13 @@ download_files () {
|
|||||||
install_service svn subversion
|
install_service svn subversion
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! command_exists jq ; then
|
||||||
|
if command_exists yum; then
|
||||||
|
rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-$REV.noarch.rpm
|
||||||
|
fi
|
||||||
|
install_service jq
|
||||||
|
fi
|
||||||
|
|
||||||
svn export --force https://github.com/${PACKAGE_SYSNAME}/${PRODUCT}/branches/${GIT_BRANCH}/build/install/docker/ ${BASE_DIR}
|
svn export --force https://github.com/${PACKAGE_SYSNAME}/${PRODUCT}/branches/${GIT_BRANCH}/build/install/docker/ ${BASE_DIR}
|
||||||
|
|
||||||
reconfigure STATUS ${STATUS}
|
reconfigure STATUS ${STATUS}
|
||||||
@ -965,8 +953,8 @@ download_files () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reconfigure () {
|
reconfigure () {
|
||||||
local VARIABLE_NAME=$1
|
local VARIABLE_NAME="$1"
|
||||||
local VARIABLE_VALUE=$2
|
local VARIABLE_VALUE="$2"
|
||||||
|
|
||||||
if [[ -n ${VARIABLE_VALUE} ]]; then
|
if [[ -n ${VARIABLE_VALUE} ]]; then
|
||||||
sed -i "s~${VARIABLE_NAME}=.*~${VARIABLE_NAME}=${VARIABLE_VALUE}~g" $BASE_DIR/.env
|
sed -i "s~${VARIABLE_NAME}=.*~${VARIABLE_NAME}=${VARIABLE_VALUE}~g" $BASE_DIR/.env
|
||||||
|
@ -7,7 +7,7 @@ privacy-breach-generic var/www/{{product}}/services/*/node_modules/*
|
|||||||
# Ignoring node_modules errors due to lack of ability to influence them
|
# Ignoring node_modules errors due to lack of ability to influence them
|
||||||
script-not-executable var/www/{{product}}/services/*/node_modules/*
|
script-not-executable var/www/{{product}}/services/*/node_modules/*
|
||||||
# Ignoring node_modules errors due to lack of ability to influence them
|
# Ignoring node_modules errors due to lack of ability to influence them
|
||||||
unusual-interpreter var/www/{{product}}/services/*/node_modules/*
|
unusual-interpreter */node_modules/*
|
||||||
|
|
||||||
# The use of the /var/www directory is caused by its past history as the default document root
|
# The use of the /var/www directory is caused by its past history as the default document root
|
||||||
dir-or-file-in-var-www
|
dir-or-file-in-var-www
|
||||||
@ -44,3 +44,12 @@ untranslatable-debconf-templates
|
|||||||
|
|
||||||
# We use this to protect sensitive information (ie passwords) in the config file
|
# We use this to protect sensitive information (ie passwords) in the config file
|
||||||
non-standard-file-perm
|
non-standard-file-perm
|
||||||
|
|
||||||
|
# There are instances where temporary or future code sections need to be retained for documentation or future development purposes
|
||||||
|
no-code-sections
|
||||||
|
|
||||||
|
# Ignoring errors due to lack of ability to influence them
|
||||||
|
library-not-linked-against-libc
|
||||||
|
|
||||||
|
# Some file triggers a privacy concern, specifically references an image files .png
|
||||||
|
privacy-breach-generic
|
||||||
|
@ -7,9 +7,6 @@ addFilter(r' W: non-standard-(uid|gid)')
|
|||||||
# This is necessary to ensure that all child packages are updated correctly
|
# This is necessary to ensure that all child packages are updated correctly
|
||||||
addFilter(r'W: requires-on-release')
|
addFilter(r'W: requires-on-release')
|
||||||
|
|
||||||
# The signature of packages occurs in further stages
|
|
||||||
addFilter(r'E: no-signature')
|
|
||||||
|
|
||||||
# The basic documentation comes with the common package
|
# The basic documentation comes with the common package
|
||||||
addFilter(r'W: no-documentation')
|
addFilter(r'W: no-documentation')
|
||||||
|
|
||||||
@ -45,3 +42,18 @@ addFilter(r'non-standard-executable-perm')
|
|||||||
|
|
||||||
# We use this to protect sensitive information (ie passwords) in the config file
|
# We use this to protect sensitive information (ie passwords) in the config file
|
||||||
addFilter(r'non-readable')
|
addFilter(r'non-readable')
|
||||||
|
|
||||||
|
# No one license from allowed pull AGPLv3, AGPLv3+ worked
|
||||||
|
addFilter(r'W: invalid-license AGPLv3')
|
||||||
|
|
||||||
|
# Сertain services require write access to the log directory. These services are launched under a user account that is different from the root user.
|
||||||
|
addFilter(r'logrotate-user-writable-log-dir')
|
||||||
|
|
||||||
|
# The use of the /var/www directory is caused by its past history as the default document root
|
||||||
|
addFilter(r'W: non-standard-dir-in-var www')
|
||||||
|
|
||||||
|
# Shared libraries centos7-librdkafka.so, libgrpc_csharp_ext.x64.so location of which is hardcoded
|
||||||
|
addFilter(r'W: binary-or-shlib-calls-gethostbyname')
|
||||||
|
|
||||||
|
# There are the same files, however on a different languages
|
||||||
|
addFilter(r'files-duplicate')
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
%package backup
|
%package backup
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Backup
|
Summary: Backup
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -8,6 +9,7 @@ AutoReqProv: no
|
|||||||
Backup
|
Backup
|
||||||
|
|
||||||
%package common
|
%package common
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Common
|
Summary: Common
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: logrotate
|
Requires: logrotate
|
||||||
@ -15,6 +17,7 @@ Requires: logrotate
|
|||||||
Common
|
Common
|
||||||
|
|
||||||
%package files-services
|
%package files-services
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Files-services
|
Summary: Files-services
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -25,6 +28,7 @@ AutoReqProv: no
|
|||||||
Files-services
|
Files-services
|
||||||
|
|
||||||
%package notify
|
%package notify
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Notify
|
Summary: Notify
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -34,6 +38,7 @@ AutoReqProv: no
|
|||||||
Notify
|
Notify
|
||||||
|
|
||||||
%package files
|
%package files
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Files
|
Summary: Files
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -43,6 +48,7 @@ AutoReqProv: no
|
|||||||
Files
|
Files
|
||||||
|
|
||||||
%package proxy
|
%package proxy
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Proxy
|
Summary: Proxy
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -53,6 +59,7 @@ AutoReqProv: no
|
|||||||
Proxy
|
Proxy
|
||||||
|
|
||||||
%package studio-notify
|
%package studio-notify
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Studio-notify
|
Summary: Studio-notify
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -62,6 +69,7 @@ AutoReqProv: no
|
|||||||
Studio-notify
|
Studio-notify
|
||||||
|
|
||||||
%package people-server
|
%package people-server
|
||||||
|
Packager: %{packager}
|
||||||
Summary: People-server
|
Summary: People-server
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -71,6 +79,7 @@ AutoReqProv: no
|
|||||||
People-server
|
People-server
|
||||||
|
|
||||||
%package socket
|
%package socket
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Socket
|
Summary: Socket
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -80,6 +89,7 @@ AutoReqProv: no
|
|||||||
Socket
|
Socket
|
||||||
|
|
||||||
%package studio
|
%package studio
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Studio
|
Summary: Studio
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -89,6 +99,7 @@ AutoReqProv: no
|
|||||||
Studio
|
Studio
|
||||||
|
|
||||||
%package api
|
%package api
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Api
|
Summary: Api
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -98,6 +109,7 @@ AutoReqProv: no
|
|||||||
Api
|
Api
|
||||||
|
|
||||||
%package api-system
|
%package api-system
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Api-system
|
Summary: Api-system
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -107,6 +119,7 @@ AutoReqProv: no
|
|||||||
Api-system
|
Api-system
|
||||||
|
|
||||||
%package ssoauth
|
%package ssoauth
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Ssoauth
|
Summary: Ssoauth
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -116,6 +129,7 @@ AutoReqProv: no
|
|||||||
Ssoauth
|
Ssoauth
|
||||||
|
|
||||||
%package clear-events
|
%package clear-events
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Clear-events
|
Summary: Clear-events
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -125,6 +139,7 @@ AutoReqProv: no
|
|||||||
Clear-events
|
Clear-events
|
||||||
|
|
||||||
%package backup-background
|
%package backup-background
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Backup-background
|
Summary: Backup-background
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -134,6 +149,7 @@ AutoReqProv: no
|
|||||||
Backup-background
|
Backup-background
|
||||||
|
|
||||||
%package radicale
|
%package radicale
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Radicale
|
Summary: Radicale
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -143,6 +159,7 @@ AutoReqProv: no
|
|||||||
Radicale
|
Radicale
|
||||||
|
|
||||||
%package doceditor
|
%package doceditor
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Doceditor
|
Summary: Doceditor
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -152,6 +169,7 @@ AutoReqProv: no
|
|||||||
Doceditor
|
Doceditor
|
||||||
|
|
||||||
%package migration-runner
|
%package migration-runner
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Migration-runner
|
Summary: Migration-runner
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -161,6 +179,7 @@ AutoReqProv: no
|
|||||||
Migration-runner
|
Migration-runner
|
||||||
|
|
||||||
%package login
|
%package login
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Login
|
Summary: Login
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
@ -170,6 +189,7 @@ AutoReqProv: no
|
|||||||
Login
|
Login
|
||||||
|
|
||||||
%package healthchecks
|
%package healthchecks
|
||||||
|
Packager: %{packager}
|
||||||
Summary: Healthchecks
|
Summary: Healthchecks
|
||||||
Group: Applications/Internet
|
Group: Applications/Internet
|
||||||
Requires: %name-common = %version-%release
|
Requires: %name-common = %version-%release
|
||||||
|
@ -14,6 +14,7 @@ AutoReqProv: no
|
|||||||
|
|
||||||
URL: http://onlyoffice.com
|
URL: http://onlyoffice.com
|
||||||
Vendor: Ascensio System SIA
|
Vendor: Ascensio System SIA
|
||||||
|
Packager: %{packager}
|
||||||
License: AGPLv3
|
License: AGPLv3
|
||||||
|
|
||||||
Source0: https://github.com/ONLYOFFICE/%{product}/archive/%GIT_BRANCH.tar.gz#/%{sourcename}.tar.gz
|
Source0: https://github.com/ONLYOFFICE/%{product}/archive/%GIT_BRANCH.tar.gz#/%{sourcename}.tar.gz
|
||||||
|
@ -434,7 +434,15 @@ public class DiscDataStore : BaseStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
var entries = Directory.GetFiles(targetDir, "*.*", SearchOption.AllDirectories);
|
var entries = Directory.GetFiles(targetDir, "*.*", SearchOption.AllDirectories);
|
||||||
var size = entries.Select(entry => _crypt.GetFileSize(entry)).Sum();
|
var size = entries.Where(r =>
|
||||||
|
{
|
||||||
|
if (QuotaController == null || string.IsNullOrEmpty(QuotaController.ExcludePattern))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !Path.GetFileName(r).StartsWith(QuotaController.ExcludePattern);
|
||||||
|
}
|
||||||
|
).Select(_crypt.GetFileSize).Sum();
|
||||||
|
|
||||||
var subDirs = Directory.GetDirectories(targetDir, "*", SearchOption.AllDirectories).ToList();
|
var subDirs = Directory.GetDirectories(targetDir, "*", SearchOption.AllDirectories).ToList();
|
||||||
subDirs.Reverse();
|
subDirs.Reverse();
|
||||||
|
@ -457,7 +457,15 @@ public class GoogleCloudStorage : BaseStorage
|
|||||||
await foreach (var obj in objToDel)
|
await foreach (var obj in objToDel)
|
||||||
{
|
{
|
||||||
await storage.DeleteObjectAsync(_bucket, obj.Name);
|
await storage.DeleteObjectAsync(_bucket, obj.Name);
|
||||||
await QuotaUsedDelete(domain, Convert.ToInt64(obj.Size));
|
|
||||||
|
if (QuotaController != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(QuotaController.ExcludePattern) ||
|
||||||
|
!Path.GetFileName(obj.Name).StartsWith(QuotaController.ExcludePattern))
|
||||||
|
{
|
||||||
|
await QuotaUsedDelete(domain, Convert.ToInt64(obj.Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,4 +36,6 @@ public interface IQuotaController
|
|||||||
void QuotaUsedSet(string module, string domain, string dataTag, long size);
|
void QuotaUsedSet(string module, string domain, string dataTag, long size);
|
||||||
|
|
||||||
void QuotaUsedCheck(long size);
|
void QuotaUsedCheck(long size);
|
||||||
|
|
||||||
|
string ExcludePattern { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -189,10 +189,10 @@ public class RackspaceCloudStorage : BaseStorage
|
|||||||
return SaveAsync(domain, path, stream, string.Empty, string.Empty, ACL.Auto, contentEncoding, cacheDays);
|
return SaveAsync(domain, path, stream, string.Empty, string.Empty, ACL.Auto, contentEncoding, cacheDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool EnableQuotaCheck(string domain)
|
private bool EnableQuotaCheck(string domain)
|
||||||
{
|
{
|
||||||
return (QuotaController != null) && !domain.EndsWith("_temp");
|
return (QuotaController != null) && !domain.EndsWith("_temp");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType,
|
public async Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType,
|
||||||
string contentDisposition, ACL acl, string contentEncoding = null, int cacheDays = 5,
|
string contentDisposition, ACL acl, string contentEncoding = null, int cacheDays = 5,
|
||||||
@ -200,7 +200,7 @@ public class RackspaceCloudStorage : BaseStorage
|
|||||||
{
|
{
|
||||||
var buffered = _tempStream.GetBuffered(stream);
|
var buffered = _tempStream.GetBuffered(stream);
|
||||||
|
|
||||||
if (EnableQuotaCheck(domain))
|
if (EnableQuotaCheck(domain))
|
||||||
{
|
{
|
||||||
QuotaController.QuotaUsedCheck(buffered.Length);
|
QuotaController.QuotaUsedCheck(buffered.Length);
|
||||||
}
|
}
|
||||||
@ -482,7 +482,15 @@ public class RackspaceCloudStorage : BaseStorage
|
|||||||
foreach (var obj in objToDel)
|
foreach (var obj in objToDel)
|
||||||
{
|
{
|
||||||
client.DeleteObject(_private_container, obj.Name);
|
client.DeleteObject(_private_container, obj.Name);
|
||||||
await QuotaUsedDelete(domain, obj.Bytes);
|
|
||||||
|
if (QuotaController != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(QuotaController.ExcludePattern) ||
|
||||||
|
!Path.GetFileName(obj.Name).StartsWith(QuotaController.ExcludePattern))
|
||||||
|
{
|
||||||
|
await QuotaUsedDelete(domain, obj.Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,7 +763,7 @@ public class RackspaceCloudStorage : BaseStorage
|
|||||||
|
|
||||||
var obj = client.ListObjects(_private_container, null, null, null, prefix, _region).FirstOrDefault();
|
var obj = client.ListObjects(_private_container, null, null, null, prefix, _region).FirstOrDefault();
|
||||||
|
|
||||||
var lastModificationDate = obj == null ? throw new FileNotFoundException("File not found" + prefix) : obj.LastModified.UtcDateTime;
|
var lastModificationDate = obj == null ? throw new FileNotFoundException("File not found" + prefix) : obj.LastModified.UtcDateTime;
|
||||||
|
|
||||||
var etag = '"' + lastModificationDate.Ticks.ToString("X8", CultureInfo.InvariantCulture) + '"';
|
var etag = '"' + lastModificationDate.Ticks.ToString("X8", CultureInfo.InvariantCulture) + '"';
|
||||||
|
|
||||||
|
@ -495,7 +495,14 @@ public class S3Storage : BaseStorage
|
|||||||
|
|
||||||
await client.DeleteObjectAsync(deleteRequest);
|
await client.DeleteObjectAsync(deleteRequest);
|
||||||
|
|
||||||
await QuotaUsedDelete(domain, s3Object.Size);
|
if (QuotaController != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(QuotaController.ExcludePattern) ||
|
||||||
|
!Path.GetFileName(s3Object.Key).StartsWith(QuotaController.ExcludePattern))
|
||||||
|
{
|
||||||
|
await QuotaUsedDelete(domain, s3Object.Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ public class TenantQuotaController : IQuotaController
|
|||||||
private readonly TenantQuotaFeatureChecker<MaxTotalSizeFeature, long> _maxTotalSizeChecker;
|
private readonly TenantQuotaFeatureChecker<MaxTotalSizeFeature, long> _maxTotalSizeChecker;
|
||||||
private Lazy<long> _lazyCurrentSize;
|
private Lazy<long> _lazyCurrentSize;
|
||||||
private long _currentSize;
|
private long _currentSize;
|
||||||
|
public string ExcludePattern { get; set; }
|
||||||
|
|
||||||
public TenantQuotaController(TenantManager tenantManager, AuthContext authContext, TenantQuotaFeatureChecker<MaxFileSizeFeature, long> maxFileSizeChecker, TenantQuotaFeatureChecker<MaxTotalSizeFeature, long> maxTotalSizeChecker)
|
public TenantQuotaController(TenantManager tenantManager, AuthContext authContext, TenantQuotaFeatureChecker<MaxFileSizeFeature, long> maxFileSizeChecker, TenantQuotaFeatureChecker<MaxTotalSizeFeature, long> maxTotalSizeChecker)
|
||||||
{
|
{
|
||||||
@ -58,13 +59,14 @@ public class TenantQuotaController : IQuotaController
|
|||||||
_maxTotalSizeChecker = maxTotalSizeChecker;
|
_maxTotalSizeChecker = maxTotalSizeChecker;
|
||||||
_authContext = authContext;
|
_authContext = authContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(int tenant)
|
public void Init(int tenant, string excludePattern = null)
|
||||||
{
|
{
|
||||||
_tenant = tenant;
|
_tenant = tenant;
|
||||||
_lazyCurrentSize = new Lazy<long>(() => _tenantManager.FindTenantQuotaRows(tenant)
|
_lazyCurrentSize = new Lazy<long>(() => _tenantManager.FindTenantQuotaRows(tenant)
|
||||||
.Where(r => UsedInQuota(r.Tag))
|
.Where(r => UsedInQuota(r.Tag))
|
||||||
.Sum(r => r.Counter));
|
.Sum(r => r.Counter));
|
||||||
|
ExcludePattern = excludePattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QuotaUsedAdd(string module, string domain, string dataTag, long size, bool quotaCheckFileSize = true)
|
public void QuotaUsedAdd(string module, string domain, string dataTag, long size, bool quotaCheckFileSize = true)
|
||||||
|
@ -394,6 +394,15 @@ public class FactoryIndexer<T> : IFactoryIndexer where T : class, ISearchItem
|
|||||||
Logger.ErrorUpdate(e);
|
Logger.ErrorUpdate(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public Task<bool> UpdateAsync(T data, UpdateAction action, Expression<Func<T, IList>> field, bool immediately = true)
|
||||||
|
{
|
||||||
|
var t = _serviceProvider.GetService<T>();
|
||||||
|
|
||||||
|
return !Support(t)
|
||||||
|
? Task.FromResult(false)
|
||||||
|
: Queue(() => _indexer.Update(data, action, field, immediately));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, bool immediately = true, params Expression<Func<T, object>>[] fields)
|
public void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, bool immediately = true, params Expression<Func<T, object>>[] fields)
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
"oidc": {
|
"oidc": {
|
||||||
"authority": ""
|
"authority": ""
|
||||||
},
|
},
|
||||||
"server-root": ""
|
"server-root": ""
|
||||||
},
|
},
|
||||||
"license": {
|
"license": {
|
||||||
"file": {
|
"file": {
|
||||||
@ -296,7 +296,8 @@
|
|||||||
"book-training-email": "training@onlyoffice.com",
|
"book-training-email": "training@onlyoffice.com",
|
||||||
"documentation-email": "documentation@onlyoffice.com",
|
"documentation-email": "documentation@onlyoffice.com",
|
||||||
"max-upload-size": 5242880,
|
"max-upload-size": 5242880,
|
||||||
"zendesk-key": ""
|
"zendesk-key": "",
|
||||||
|
"samesite": ""
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"default": {
|
"default": {
|
||||||
@ -342,7 +343,8 @@
|
|||||||
"storageBucket": "",
|
"storageBucket": "",
|
||||||
"messagingSenderId": "",
|
"messagingSenderId": "",
|
||||||
"appId": "",
|
"appId": "",
|
||||||
"measurementId": ""
|
"measurementId": "",
|
||||||
|
"databaseURL": ""
|
||||||
},
|
},
|
||||||
"debug-info": {
|
"debug-info": {
|
||||||
"enabled": "true"
|
"enabled": "true"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docspace",
|
"name": "docspace",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@docspace/client",
|
"name": "@docspace/client",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -487,6 +487,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
|||||||
"/files/trash/filter",
|
"/files/trash/filter",
|
||||||
|
|
||||||
"/accounts",
|
"/accounts",
|
||||||
|
"/accounts/changeOwner",
|
||||||
"/accounts/filter",
|
"/accounts/filter",
|
||||||
|
|
||||||
"/accounts/create/:type",
|
"/accounts/create/:type",
|
||||||
|
165
packages/client/src/components/dialogs/ReportDialog/index.js
Normal file
165
packages/client/src/components/dialogs/ReportDialog/index.js
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import FileReactSvgUrl from "PUBLIC_DIR/images/icons/24/file.svg?url";
|
||||||
|
import DownloadReactSvgUrl from "PUBLIC_DIR/images/download.react.svg?url";
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { inject, observer } from "mobx-react";
|
||||||
|
import { isMobileOnly } from "react-device-detect";
|
||||||
|
|
||||||
|
import ModalDialog from "@docspace/components/modal-dialog";
|
||||||
|
import Text from "@docspace/components/text";
|
||||||
|
import Button from "@docspace/components/button";
|
||||||
|
import Textarea from "@docspace/components/textarea";
|
||||||
|
import IconButton from "@docspace/components/icon-button";
|
||||||
|
import toastr from "@docspace/components/toast/toastr";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCrashReport,
|
||||||
|
downloadJson,
|
||||||
|
getCurrentDate,
|
||||||
|
} from "SRC_DIR/helpers/crashReport";
|
||||||
|
|
||||||
|
const ModalDialogContainer = styled(ModalDialog)`
|
||||||
|
#modal-dialog {
|
||||||
|
width: auto;
|
||||||
|
max-width: 520px;
|
||||||
|
max-height: 560px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-description {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-wrapper {
|
||||||
|
margin-top: 8px;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.report-filename {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
width: 24px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ReportDialog = (props) => {
|
||||||
|
const { t, ready } = useTranslation(["Common"]);
|
||||||
|
const { visible, onClose, error, user, version, FirebaseHelper } = props;
|
||||||
|
const [report, setReport] = useState({});
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const report = getCrashReport(user.id, version, user.cultureName, error);
|
||||||
|
setReport(report);
|
||||||
|
console.log(report);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onChangeTextareaValue = (e) => {
|
||||||
|
setDescription(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickDownload = () => {
|
||||||
|
downloadJson(report, fileTitle);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickSend = async () => {
|
||||||
|
try {
|
||||||
|
const reportWithDescription = Object.assign(report, {
|
||||||
|
description: description,
|
||||||
|
});
|
||||||
|
await FirebaseHelper.sendCrashReport(reportWithDescription);
|
||||||
|
toastr.success(t("ErrorReportSuccess"));
|
||||||
|
onCloseAction();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
toastr.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseAction = () => {
|
||||||
|
setDescription("");
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileTitle = t("ErrorReport") + " " + getCurrentDate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalDialogContainer
|
||||||
|
isLoading={!ready}
|
||||||
|
visible={visible}
|
||||||
|
onClose={onCloseAction}
|
||||||
|
displayType="modal"
|
||||||
|
>
|
||||||
|
<ModalDialog.Header>{t("ErrorReport")}</ModalDialog.Header>
|
||||||
|
<ModalDialog.Body>
|
||||||
|
<Text className="report-description" noSelect>
|
||||||
|
{t("ErrorReportDescription")}
|
||||||
|
</Text>
|
||||||
|
<Textarea
|
||||||
|
placeholder={t("RecoverDescribeYourProblemPlaceholder")}
|
||||||
|
value={description}
|
||||||
|
onChange={onChangeTextareaValue}
|
||||||
|
autoFocus
|
||||||
|
areaSelect
|
||||||
|
heightTextArea={72}
|
||||||
|
fontSize={13}
|
||||||
|
/>
|
||||||
|
<div className="report-wrapper">
|
||||||
|
<img src={FileReactSvgUrl} className="file-icon" />
|
||||||
|
<Text as="div" fontWeight={600} noSelect className="report-filename">
|
||||||
|
{fileTitle}
|
||||||
|
<Text fontWeight={600} noSelect color="#A3A9AE">
|
||||||
|
.json
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<IconButton
|
||||||
|
className="icon-button"
|
||||||
|
iconName={DownloadReactSvgUrl}
|
||||||
|
size="16"
|
||||||
|
isfill={true}
|
||||||
|
onClick={onClickDownload}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ModalDialog.Body>
|
||||||
|
<ModalDialog.Footer>
|
||||||
|
<Button
|
||||||
|
key="SendButton"
|
||||||
|
label={t("SendButton")}
|
||||||
|
size="normal"
|
||||||
|
primary
|
||||||
|
scale={isMobileOnly}
|
||||||
|
onClick={onClickSend}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
key="CancelButton"
|
||||||
|
label={t("CancelButton")}
|
||||||
|
size="normal"
|
||||||
|
scale={isMobileOnly}
|
||||||
|
onClick={onCloseAction}
|
||||||
|
/>
|
||||||
|
</ModalDialog.Footer>
|
||||||
|
</ModalDialogContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default inject(({ auth }) => {
|
||||||
|
const { user } = auth.userStore;
|
||||||
|
const { firebaseHelper } = auth.settingsStore;
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
version: auth.version,
|
||||||
|
FirebaseHelper: firebaseHelper,
|
||||||
|
};
|
||||||
|
})(observer(ReportDialog));
|
@ -29,6 +29,7 @@ import LogoutConnectionDialog from "./LogoutConnectionDialog";
|
|||||||
import LogoutAllConnectionDialog from "./LogoutAllConnectionDialog";
|
import LogoutAllConnectionDialog from "./LogoutAllConnectionDialog";
|
||||||
import CreateRoomConfirmDialog from "./CreateRoomConfirmDialog";
|
import CreateRoomConfirmDialog from "./CreateRoomConfirmDialog";
|
||||||
import PortalRenamingDialog from "./PortalRenamingDialog";
|
import PortalRenamingDialog from "./PortalRenamingDialog";
|
||||||
|
import ReportDialog from "./ReportDialog";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
EmptyTrashDialog,
|
EmptyTrashDialog,
|
||||||
@ -62,4 +63,5 @@ export {
|
|||||||
InviteUsersWarningDialog,
|
InviteUsersWarningDialog,
|
||||||
LogoutAllConnectionDialog,
|
LogoutAllConnectionDialog,
|
||||||
PortalRenamingDialog,
|
PortalRenamingDialog,
|
||||||
|
ReportDialog,
|
||||||
};
|
};
|
||||||
|
37
packages/client/src/helpers/crashReport.js
Normal file
37
packages/client/src/helpers/crashReport.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import saveAs from "file-saver";
|
||||||
|
|
||||||
|
export const getCrashReport = (userId, version, language, error) => {
|
||||||
|
const currentTime = new Date();
|
||||||
|
const reportTime = currentTime.toTimeString();
|
||||||
|
const lsObject = JSON.stringify(window.localStorage) || "";
|
||||||
|
|
||||||
|
const report = {
|
||||||
|
url: window.origin,
|
||||||
|
userId: userId,
|
||||||
|
version: version,
|
||||||
|
platform: navigator?.platform,
|
||||||
|
userAgent: navigator?.userAgent,
|
||||||
|
language: language || "en",
|
||||||
|
errorMessage: error?.message,
|
||||||
|
errorStack: error?.stack,
|
||||||
|
localStorage: lsObject,
|
||||||
|
reportTime: reportTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
return report;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const downloadJson = (json, fileName) => {
|
||||||
|
const cleanJson = JSON.stringify(json);
|
||||||
|
const data = new Blob([cleanJson], { type: "application/json" });
|
||||||
|
const url = window.URL.createObjectURL(data);
|
||||||
|
saveAs(url, `${fileName}.json`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurrentDate = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = now.getMonth() + 1;
|
||||||
|
const day = now.getDate();
|
||||||
|
return `${day}.${month}.${year}`;
|
||||||
|
};
|
@ -26,10 +26,16 @@ const PeopleSection = React.memo(() => {
|
|||||||
/>
|
/>
|
||||||
<PrivateRoute
|
<PrivateRoute
|
||||||
exact
|
exact
|
||||||
withManager
|
withManager
|
||||||
path={["/accounts"]}
|
path={["/accounts"]}
|
||||||
component={HomeRedirectToFilter}
|
component={HomeRedirectToFilter}
|
||||||
/>
|
/>
|
||||||
|
<PrivateRoute
|
||||||
|
exact
|
||||||
|
withManager
|
||||||
|
path={["/accounts/changeOwner"]}
|
||||||
|
component={RedirectWithChangeOwnerDialog}
|
||||||
|
/>
|
||||||
<PrivateRoute
|
<PrivateRoute
|
||||||
path={"/accounts/filter"}
|
path={"/accounts/filter"}
|
||||||
withManager
|
withManager
|
||||||
@ -55,6 +61,20 @@ const HomeRedirectToFilter = (props) => {
|
|||||||
return <Redirect to={`/accounts/filter?${urlFilter}`} />;
|
return <Redirect to={`/accounts/filter?${urlFilter}`} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RedirectWithChangeOwnerDialog = (props) => {
|
||||||
|
const filter = Filter.getDefault();
|
||||||
|
const urlFilter = filter.toUrlParams();
|
||||||
|
return (
|
||||||
|
<Redirect
|
||||||
|
to={{
|
||||||
|
pathname: "/accounts/filter",
|
||||||
|
search: `?${urlFilter}`,
|
||||||
|
state: { openChangeOwnerDialog: true },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const PeopleContent = (props) => {
|
const PeopleContent = (props) => {
|
||||||
const { loadBaseInfo, isLoading, setFirstLoad } = props;
|
const { loadBaseInfo, isLoading, setFirstLoad } = props;
|
||||||
|
|
||||||
|
@ -44,6 +44,8 @@ const PureHome = ({
|
|||||||
withPaging,
|
withPaging,
|
||||||
onClickBack,
|
onClickBack,
|
||||||
setPortalTariff,
|
setPortalTariff,
|
||||||
|
|
||||||
|
setChangeOwnerDialogVisible,
|
||||||
}) => {
|
}) => {
|
||||||
const { location } = history;
|
const { location } = history;
|
||||||
const { pathname } = location;
|
const { pathname } = location;
|
||||||
@ -73,6 +75,9 @@ const PureHome = ({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setIsRefresh(false);
|
setIsRefresh(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (location?.state?.openChangeOwnerDialog)
|
||||||
|
setChangeOwnerDialogVisible(true);
|
||||||
}
|
}
|
||||||
}, [pathname, location, setSelectedNode]);
|
}, [pathname, location, setSelectedNode]);
|
||||||
|
|
||||||
@ -140,24 +145,15 @@ export default inject(
|
|||||||
const { settingsStore, currentTariffStatusStore } = auth;
|
const { settingsStore, currentTariffStatusStore } = auth;
|
||||||
const { setPortalTariff } = currentTariffStatusStore;
|
const { setPortalTariff } = currentTariffStatusStore;
|
||||||
const { showCatalog, withPaging } = settingsStore;
|
const { showCatalog, withPaging } = settingsStore;
|
||||||
const {
|
const { usersStore, selectedGroupStore, loadingStore, viewAs } =
|
||||||
usersStore,
|
peopleStore;
|
||||||
selectedGroupStore,
|
|
||||||
loadingStore,
|
|
||||||
viewAs,
|
|
||||||
} = peopleStore;
|
|
||||||
const { getUsersList } = usersStore;
|
const { getUsersList } = usersStore;
|
||||||
const { selectedGroup } = selectedGroupStore;
|
const { selectedGroup } = selectedGroupStore;
|
||||||
const { setSelectedNode } = treeFoldersStore;
|
const { setSelectedNode } = treeFoldersStore;
|
||||||
const { onClickBack } = filesActionsStore;
|
const { onClickBack } = filesActionsStore;
|
||||||
const {
|
const { isLoading, setIsLoading, setIsRefresh, firstLoad, setFirstLoad } =
|
||||||
isLoading,
|
loadingStore;
|
||||||
setIsLoading,
|
const { setChangeOwnerDialogVisible } = peopleStore.dialogStore;
|
||||||
setIsRefresh,
|
|
||||||
firstLoad,
|
|
||||||
setFirstLoad,
|
|
||||||
} = loadingStore;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setPortalTariff,
|
setPortalTariff,
|
||||||
isAdmin: auth.isAdmin,
|
isAdmin: auth.isAdmin,
|
||||||
@ -176,6 +172,7 @@ export default inject(
|
|||||||
snackbarExist: auth.settingsStore.snackbarExist,
|
snackbarExist: auth.settingsStore.snackbarExist,
|
||||||
withPaging,
|
withPaging,
|
||||||
onClickBack,
|
onClickBack,
|
||||||
|
setChangeOwnerDialogVisible,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)(observer(withRouter(Home)));
|
)(observer(withRouter(Home)));
|
||||||
|
@ -1,23 +1,95 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
import styled from "styled-components";
|
||||||
import ErrorContainer from "@docspace/common/components/ErrorContainer";
|
import { inject, observer } from "mobx-react";
|
||||||
import { I18nextProvider, useTranslation } from "react-i18next";
|
import { I18nextProvider, useTranslation } from "react-i18next";
|
||||||
|
import ErrorContainer from "@docspace/common/components/ErrorContainer";
|
||||||
|
import Link from "@docspace/components/link";
|
||||||
|
import { ZendeskAPI } from "@docspace/common/components/Zendesk";
|
||||||
|
|
||||||
|
import { ReportDialog } from "SRC_DIR/components/dialogs";
|
||||||
|
import DocspaceLogo from "SRC_DIR/DocspaceLogo";
|
||||||
|
|
||||||
import i18n from "./i18n";
|
import i18n from "./i18n";
|
||||||
const Error520 = ({ match }) => {
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 16px;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Error520 = ({ errorLog, currentColorScheme, FirebaseHelper }) => {
|
||||||
const { t } = useTranslation(["Common"]);
|
const { t } = useTranslation(["Common"]);
|
||||||
const { error } = (match && match.params) || {};
|
|
||||||
|
const [reportDialogVisible, setReportDialogVisible] = useState(false);
|
||||||
|
|
||||||
|
const showDialog = () => {
|
||||||
|
setReportDialogVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
setReportDialogVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onReloadClick = () => {
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
ZendeskAPI("webWidget", "show");
|
||||||
|
|
||||||
|
if (!FirebaseHelper.isEnabledDB)
|
||||||
|
return <ErrorContainer headerText={t("SomethingWentWrong")} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorContainer headerText={t("SomethingWentWrong")} bodyText={error} />
|
<StyledWrapper>
|
||||||
|
<DocspaceLogo className="logo" />
|
||||||
|
<ErrorContainer
|
||||||
|
isPrimaryButton={false}
|
||||||
|
headerText={t("SomethingWentWrong")}
|
||||||
|
customizedBodyText={t("SomethingWentWrongDescription")}
|
||||||
|
buttonText={t("SendReport")}
|
||||||
|
onClickButton={showDialog}
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
className="link"
|
||||||
|
type="action"
|
||||||
|
isHovered
|
||||||
|
fontWeight={600}
|
||||||
|
onClick={onReloadClick}
|
||||||
|
color={currentColorScheme?.main?.accent}
|
||||||
|
>
|
||||||
|
{t("ReloadPage")}
|
||||||
|
</Link>
|
||||||
|
<ReportDialog
|
||||||
|
visible={reportDialogVisible}
|
||||||
|
onClose={closeDialog}
|
||||||
|
error={errorLog}
|
||||||
|
/>
|
||||||
|
</StyledWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Error520.propTypes = {
|
const Error520Wrapper = inject(({ auth }) => {
|
||||||
match: PropTypes.object,
|
const { currentColorScheme, firebaseHelper } = auth.settingsStore;
|
||||||
};
|
|
||||||
|
|
||||||
export default () => (
|
return {
|
||||||
|
currentColorScheme,
|
||||||
|
FirebaseHelper: firebaseHelper,
|
||||||
|
};
|
||||||
|
})(observer(Error520));
|
||||||
|
|
||||||
|
export default (props) => (
|
||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<Error520 />
|
<Error520Wrapper {...props} />
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
);
|
);
|
||||||
|
@ -132,7 +132,12 @@ const FilesSection = React.memo(({ withMainButton }) => {
|
|||||||
exact
|
exact
|
||||||
restricted
|
restricted
|
||||||
withManager
|
withManager
|
||||||
path={["/accounts", "/accounts/filter", "/accounts/create/:type"]}
|
path={[
|
||||||
|
"/accounts",
|
||||||
|
"/accounts/changeOwner",
|
||||||
|
"/accounts/filter",
|
||||||
|
"/accounts/create/:type",
|
||||||
|
]}
|
||||||
component={Accounts}
|
component={Accounts}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -90,11 +90,11 @@ const PersonalSettings = ({
|
|||||||
hideAdminSettings={!showAdminSettings}
|
hideAdminSettings={!showAdminSettings}
|
||||||
>
|
>
|
||||||
<Box className="settings-section">
|
<Box className="settings-section">
|
||||||
{showTitle && (
|
{/* {showTitle && (
|
||||||
<Heading className="heading" level={2} size="xsmall">
|
<Heading className="heading" level={2} size="xsmall">
|
||||||
{t("Common:Common")}
|
{t("Common:Common")}
|
||||||
</Heading>
|
</Heading>
|
||||||
)}
|
)} */}
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
className="toggle-btn"
|
className="toggle-btn"
|
||||||
label={thumbnailsSizeLabel}
|
label={thumbnailsSizeLabel}
|
||||||
@ -124,6 +124,14 @@ const PersonalSettings = ({
|
|||||||
isChecked={confirmDelete}
|
isChecked={confirmDelete}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{!isVisitor && (
|
||||||
|
<ToggleButton
|
||||||
|
className="toggle-btn"
|
||||||
|
label={t("UpdateOrCreate")}
|
||||||
|
onChange={onChangeUpdateIfExist}
|
||||||
|
isChecked={updateIfExist}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* <Box className="settings-section">
|
{/* <Box className="settings-section">
|
||||||
@ -154,7 +162,7 @@ const PersonalSettings = ({
|
|||||||
/>
|
/>
|
||||||
</Box> */}
|
</Box> */}
|
||||||
|
|
||||||
{!isVisitor && (
|
{/* {!isVisitor && (
|
||||||
<Box className="settings-section">
|
<Box className="settings-section">
|
||||||
<Heading className="heading" level={2} size="xsmall">
|
<Heading className="heading" level={2} size="xsmall">
|
||||||
{t("StoringFileVersion")}
|
{t("StoringFileVersion")}
|
||||||
@ -176,7 +184,7 @@ const PersonalSettings = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)} */}
|
||||||
</StyledSettings>
|
</StyledSettings>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -63,25 +63,25 @@ const SectionBodyContent = ({ isErrorSettings, history, user }) => {
|
|||||||
[setting, history]
|
[setting, history]
|
||||||
);
|
);
|
||||||
|
|
||||||
const showAdminSettings = user.isAdmin || user.isOwner;
|
//const showAdminSettings = user.isAdmin || user.isOwner;
|
||||||
|
|
||||||
return isErrorSettings ? (
|
return isErrorSettings ? (
|
||||||
<Error520 />
|
<Error520 />
|
||||||
) : (
|
) : (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
{!showAdminSettings ? (
|
{/* {!showAdminSettings ? ( */}
|
||||||
<PersonalSettings
|
<PersonalSettings
|
||||||
t={t}
|
t={t}
|
||||||
showTitle={true}
|
showTitle={true}
|
||||||
showAdminSettings={showAdminSettings}
|
showAdminSettings={false} //showAdminSettings
|
||||||
/>
|
/>
|
||||||
) : (
|
{/* ) : (
|
||||||
<Submenu
|
<Submenu
|
||||||
data={data}
|
data={data}
|
||||||
startSelect={setting === "common" ? commonSettings : adminSettings}
|
startSelect={setting === "common" ? commonSettings : adminSettings}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
)}
|
)} */}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -261,6 +261,10 @@ const Wizard = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClickRetry = () => {
|
||||||
|
window.location.href = "/";
|
||||||
|
};
|
||||||
|
|
||||||
if (!isWizardLoaded)
|
if (!isWizardLoaded)
|
||||||
return <Loader className="pageLoader" type="rombs" size="40px" />;
|
return <Loader className="pageLoader" type="rombs" size="40px" />;
|
||||||
|
|
||||||
@ -270,7 +274,7 @@ const Wizard = (props) => {
|
|||||||
headerText={t("Common:SomethingWentWrong")}
|
headerText={t("Common:SomethingWentWrong")}
|
||||||
bodyText={t("ErrorInitWizard")}
|
bodyText={t("ErrorInitWizard")}
|
||||||
buttonText={t("ErrorInitWizardButton")}
|
buttonText={t("ErrorInitWizardButton")}
|
||||||
buttonUrl="/"
|
onClickButton={onClickRetry}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2163,6 +2163,8 @@ class FilesActionStore {
|
|||||||
id = urlFilter.folder;
|
id = urlFilter.folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id === undefined) return;
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
fetchFiles(id, null, true, false).finally(() => setIsLoading(false));
|
fetchFiles(id, null, true, false).finally(() => setIsLoading(false));
|
||||||
|
@ -5,13 +5,13 @@ import Error520 from "client/Error520";
|
|||||||
class ErrorBoundary extends React.Component {
|
class ErrorBoundary extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { hasError: false };
|
this.state = { error: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
static getDerivedStateFromError(error) {
|
static getDerivedStateFromError(error) {
|
||||||
// Update state so the next render will show the fallback UI.
|
// Update state so the next render will show the fallback UI.
|
||||||
return { hasError: true };
|
return { error: error ?? "Unhandled exception" };
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch(error, errorInfo) {
|
componentDidCatch(error, errorInfo) {
|
||||||
@ -21,9 +21,9 @@ class ErrorBoundary extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.error) {
|
||||||
// You can render any custom fallback UI
|
// You can render any custom fallback UI
|
||||||
return <Error520 />;
|
return <Error520 errorLog={this.state.error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.children;
|
return this.props.children;
|
||||||
|
@ -12,9 +12,10 @@ const ErrorContainer = (props) => {
|
|||||||
headerText,
|
headerText,
|
||||||
bodyText,
|
bodyText,
|
||||||
buttonText,
|
buttonText,
|
||||||
buttonUrl,
|
onClickButton,
|
||||||
children,
|
children,
|
||||||
customizedBodyText,
|
customizedBodyText,
|
||||||
|
isPrimaryButton,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -354,16 +355,16 @@ const ErrorContainer = (props) => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{buttonText && buttonUrl && (
|
{buttonText && onClickButton && (
|
||||||
<div id="button-container">
|
<div id="button-container">
|
||||||
<Button
|
<Button
|
||||||
theme={rest?.theme}
|
theme={rest?.theme}
|
||||||
id="button"
|
id="button"
|
||||||
size="normal"
|
size="normal"
|
||||||
scale
|
scale
|
||||||
primary
|
primary={isPrimaryButton}
|
||||||
label={buttonText}
|
label={buttonText}
|
||||||
onClick={() => (window.location.href = buttonUrl)}
|
onClick={onClickButton}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -372,11 +373,16 @@ const ErrorContainer = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ErrorContainer.defaultProps = {
|
||||||
|
isPrimaryButton: true,
|
||||||
|
};
|
||||||
|
|
||||||
ErrorContainer.propTypes = {
|
ErrorContainer.propTypes = {
|
||||||
headerText: PropTypes.string,
|
headerText: PropTypes.string,
|
||||||
bodyText: PropTypes.string,
|
bodyText: PropTypes.string,
|
||||||
|
isPrimaryButton: PropTypes.bool,
|
||||||
buttonText: PropTypes.string,
|
buttonText: PropTypes.string,
|
||||||
buttonUrl: PropTypes.string,
|
onClickButton: PropTypes.func,
|
||||||
children: PropTypes.any,
|
children: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ storiesOf("Components| ErrorContainer", module)
|
|||||||
"This page was removed, renamed or doesn’t exist anymore."
|
"This page was removed, renamed or doesn’t exist anymore."
|
||||||
)}
|
)}
|
||||||
buttonText={text("buttonText", "Return to homepage")}
|
buttonText={text("buttonText", "Return to homepage")}
|
||||||
buttonUrl={text("buttonUrl", "/")}
|
onClickButton={() => console.log("click")}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
@ -15,7 +15,6 @@ describe("<ErrorContainer />", () => {
|
|||||||
headerText="Some error has happened"
|
headerText="Some error has happened"
|
||||||
bodyText="Try again later"
|
bodyText="Try again later"
|
||||||
buttonText="Go back"
|
buttonText="Go back"
|
||||||
buttonUrl="/page"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ describe("<ErrorContainer />", () => {
|
|||||||
expect(wrapper.prop("headerText")).toEqual("Some error has happened");
|
expect(wrapper.prop("headerText")).toEqual("Some error has happened");
|
||||||
expect(wrapper.prop("bodyText")).toEqual("Try again later");
|
expect(wrapper.prop("bodyText")).toEqual("Try again later");
|
||||||
expect(wrapper.prop("buttonText")).toEqual("Go back");
|
expect(wrapper.prop("buttonText")).toEqual("Go back");
|
||||||
expect(wrapper.prop("buttonUrl")).toEqual("/page");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("accepts id", () => {
|
it("accepts id", () => {
|
||||||
|
@ -10,7 +10,7 @@ const StyledErrorContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0 16px;
|
||||||
padding-top: 36px;
|
padding-top: 36px;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -44,16 +44,14 @@ const StyledErrorContainer = styled.div`
|
|||||||
}
|
}
|
||||||
|
|
||||||
#button-container {
|
#button-container {
|
||||||
margin: 0 0 auto 0;
|
width: 320px;
|
||||||
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#button {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 0 36px 0;
|
|
||||||
}
|
|
||||||
#customized-text {
|
#customized-text {
|
||||||
color: ${(props) => props.theme.errorContainer.bodyText};
|
color: ${(props) => props.theme.errorContainer.bodyText};
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 960px) {
|
@media screen and (max-width: 960px) {
|
||||||
body {
|
body {
|
||||||
padding: 24px 24px 0 24px;
|
padding: 24px 24px 0 24px;
|
||||||
@ -62,10 +60,6 @@ const StyledErrorContainer = styled.div`
|
|||||||
#container {
|
#container {
|
||||||
margin: 12px 0 48px 0;
|
margin: 12px 0 48px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#button {
|
|
||||||
margin: 0 0 24px 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 620px) {
|
@media screen and (max-width: 620px) {
|
||||||
@ -88,13 +82,11 @@ const StyledErrorContainer = styled.div`
|
|||||||
}
|
}
|
||||||
|
|
||||||
#button-container {
|
#button-container {
|
||||||
align-self: stretch;
|
width: 100%;
|
||||||
margin: auto 0 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#button {
|
#button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 0 18px 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@docspace/common",
|
"name": "@docspace/common",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "echo 'skip it'",
|
"build": "echo 'skip it'",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import firebase from "firebase/app";
|
import firebase from "firebase/app";
|
||||||
import "firebase/remote-config";
|
import "firebase/remote-config";
|
||||||
import "firebase/storage";
|
import "firebase/storage";
|
||||||
|
import "firebase/database";
|
||||||
|
|
||||||
import CampaignsCloudPngUrl from "PUBLIC_DIR/images/campaigns.cloud.png";
|
import CampaignsCloudPngUrl from "PUBLIC_DIR/images/campaigns.cloud.png";
|
||||||
import CampaignsDesktopPngUrl from "PUBLIC_DIR/images/campaigns.desktop.png";
|
import CampaignsDesktopPngUrl from "PUBLIC_DIR/images/campaigns.desktop.png";
|
||||||
@ -12,6 +13,7 @@ class FirebaseHelper {
|
|||||||
remoteConfig = null;
|
remoteConfig = null;
|
||||||
firebaseConfig = null;
|
firebaseConfig = null;
|
||||||
firebaseStorage = null;
|
firebaseStorage = null;
|
||||||
|
firebaseDB = null;
|
||||||
|
|
||||||
constructor(settings) {
|
constructor(settings) {
|
||||||
this.firebaseConfig = settings;
|
this.firebaseConfig = settings;
|
||||||
@ -28,6 +30,8 @@ class FirebaseHelper {
|
|||||||
|
|
||||||
this.remoteConfig = firebase.remoteConfig();
|
this.remoteConfig = firebase.remoteConfig();
|
||||||
|
|
||||||
|
this.firebaseDB = firebase.database();
|
||||||
|
|
||||||
this.remoteConfig.settings = {
|
this.remoteConfig.settings = {
|
||||||
fetchTimeoutMillis: 3600000,
|
fetchTimeoutMillis: 3600000,
|
||||||
minimumFetchIntervalMillis: 3600000,
|
minimumFetchIntervalMillis: 3600000,
|
||||||
@ -64,6 +68,10 @@ class FirebaseHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isEnabledDB() {
|
||||||
|
return this.isEnabled && this.config["databaseUrl"];
|
||||||
|
}
|
||||||
|
|
||||||
async checkMaintenance() {
|
async checkMaintenance() {
|
||||||
if (!this.isEnabled) return Promise.reject("Not enabled");
|
if (!this.isEnabled) return Promise.reject("Not enabled");
|
||||||
|
|
||||||
@ -145,6 +153,16 @@ class FirebaseHelper {
|
|||||||
const domain = this.config["authDomain"];
|
const domain = this.config["authDomain"];
|
||||||
return `https://${domain}/locales/${lng}/CampaignPersonal${banner}.json`;
|
return `https://${domain}/locales/${lng}/CampaignPersonal${banner}.json`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendCrashReport(report) {
|
||||||
|
try {
|
||||||
|
const reportListRef = this.firebaseDB.ref("reports");
|
||||||
|
const neReportRef = reportListRef.push();
|
||||||
|
neReportRef.set(report);
|
||||||
|
} catch (error) {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FirebaseHelper;
|
export default FirebaseHelper;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@docspace/components",
|
"name": "@docspace/components",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "echo 'skip it'",
|
"build": "echo 'skip it'",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@docspace/editor",
|
"name": "@docspace/editor",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "/doceditor",
|
"homepage": "/doceditor",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@docspace/login",
|
"name": "@docspace/login",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "/login",
|
"homepage": "/login",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -46,6 +46,8 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
private readonly ThumbnailSettings _thumbnailSettings;
|
private readonly ThumbnailSettings _thumbnailSettings;
|
||||||
private readonly IQuotaService _quotaService;
|
private readonly IQuotaService _quotaService;
|
||||||
|
private readonly StorageFactory _storageFactory;
|
||||||
|
private readonly TenantQuotaController _tenantQuotaController;
|
||||||
|
|
||||||
public FileDao(
|
public FileDao(
|
||||||
ILogger<FileDao> logger,
|
ILogger<FileDao> logger,
|
||||||
@ -73,7 +75,9 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
Settings settings,
|
Settings settings,
|
||||||
IMapper mapper,
|
IMapper mapper,
|
||||||
ThumbnailSettings thumbnailSettings,
|
ThumbnailSettings thumbnailSettings,
|
||||||
IQuotaService quotaService)
|
IQuotaService quotaService,
|
||||||
|
StorageFactory storageFactory,
|
||||||
|
TenantQuotaController tenantQuotaController)
|
||||||
: base(
|
: base(
|
||||||
dbContextManager,
|
dbContextManager,
|
||||||
userManager,
|
userManager,
|
||||||
@ -102,6 +106,8 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_thumbnailSettings = thumbnailSettings;
|
_thumbnailSettings = thumbnailSettings;
|
||||||
_quotaService = quotaService;
|
_quotaService = quotaService;
|
||||||
|
_storageFactory = storageFactory;
|
||||||
|
_tenantQuotaController = tenantQuotaController;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task InvalidateCacheAsync(int fileId)
|
public Task InvalidateCacheAsync(int fileId)
|
||||||
@ -559,7 +565,7 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(uploadSession != null)
|
if (uploadSession != null)
|
||||||
{
|
{
|
||||||
await _chunkedUploadSessionHolder.MoveAsync(uploadSession, GetUniqFilePath(file));
|
await _chunkedUploadSessionHolder.MoveAsync(uploadSession, GetUniqFilePath(file));
|
||||||
}
|
}
|
||||||
@ -795,7 +801,10 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
|
|
||||||
if (deleteFolder)
|
if (deleteFolder)
|
||||||
{
|
{
|
||||||
await DeleteFolderAsync(fileId);
|
var tenantId = _tenantManager.GetCurrentTenant().Id;
|
||||||
|
_tenantQuotaController.Init(tenantId, ThumbnailTitle);
|
||||||
|
var store = _storageFactory.GetStorage(tenantId, FileConstant.StorageModule, _tenantQuotaController);
|
||||||
|
await store.DeleteDirectoryAsync(GetUniqFileDirectory(fileId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toDeleteFile != null)
|
if (toDeleteFile != null)
|
||||||
@ -919,7 +928,7 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
.OrderByDescending(r => r.Level)
|
.OrderByDescending(r => r.Level)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
_factoryIndexer.Update(toUpdateFile, UpdateAction.Replace, w => w.Folders);
|
_ = _factoryIndexer.UpdateAsync(toUpdateFile, UpdateAction.Replace, w => w.Folders);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1318,11 +1327,6 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteFolderAsync(int fileId)
|
|
||||||
{
|
|
||||||
await _globalStore.GetStore().DeleteDirectoryAsync(GetUniqFileDirectory(fileId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> IsExistOnStorageAsync(File<int> file)
|
public Task<bool> IsExistOnStorageAsync(File<int> file)
|
||||||
{
|
{
|
||||||
return _globalStore.GetStore().IsFileAsync(string.Empty, GetUniqFilePath(file));
|
return _globalStore.GetStore().IsFileAsync(string.Empty, GetUniqFilePath(file));
|
||||||
|
@ -104,6 +104,9 @@
|
|||||||
"EnterName": "Enter name",
|
"EnterName": "Enter name",
|
||||||
"Error": "Error",
|
"Error": "Error",
|
||||||
"ErrorInternalServer": "Internal server error. Try again later.",
|
"ErrorInternalServer": "Internal server error. Try again later.",
|
||||||
|
"ErrorReport": "Error report",
|
||||||
|
"ErrorReportDescription": "Open the report below to see what data is included. Error reports do not contain any personal data of the users. To help our team better understand the problem, describe it in the free form using the comment field",
|
||||||
|
"ErrorReportSuccess": "Error report was successfully sent",
|
||||||
"Exabyte": "EB",
|
"Exabyte": "EB",
|
||||||
"FeedbackAndSupport": "Feedback & Support",
|
"FeedbackAndSupport": "Feedback & Support",
|
||||||
"FillFormButton": "Fill in the form",
|
"FillFormButton": "Fill in the form",
|
||||||
@ -198,6 +201,7 @@
|
|||||||
"RecoverDescribeYourProblemPlaceholder": "Describe your problem",
|
"RecoverDescribeYourProblemPlaceholder": "Describe your problem",
|
||||||
"RecoverTitle": "Access recovery",
|
"RecoverTitle": "Access recovery",
|
||||||
"RegistrationEmail": "Your registration email address",
|
"RegistrationEmail": "Your registration email address",
|
||||||
|
"ReloadPage": "Reload page",
|
||||||
"Rename": "Rename",
|
"Rename": "Rename",
|
||||||
"RepeatInvitation": "Repeat invitation",
|
"RepeatInvitation": "Repeat invitation",
|
||||||
"RequiredField": "Required field",
|
"RequiredField": "Required field",
|
||||||
@ -220,8 +224,9 @@
|
|||||||
"SelectDOCXFormat": "Select .DOCX file",
|
"SelectDOCXFormat": "Select .DOCX file",
|
||||||
"SelectFile": "Select file",
|
"SelectFile": "Select file",
|
||||||
"SendButton": "Send",
|
"SendButton": "Send",
|
||||||
"Sending": "Sending...",
|
"SendReport": "Send report",
|
||||||
"SendRequest": "Send request",
|
"SendRequest": "Send request",
|
||||||
|
"Sending": "Sending...",
|
||||||
"Sessions": "Sessions",
|
"Sessions": "Sessions",
|
||||||
"Settings": "Settings",
|
"Settings": "Settings",
|
||||||
"SettingsDocSpace": "DocSpace Settings",
|
"SettingsDocSpace": "DocSpace Settings",
|
||||||
@ -237,6 +242,7 @@
|
|||||||
"Size": "Size",
|
"Size": "Size",
|
||||||
"SizeImageLarge": "The image size is too large, please select another image.",
|
"SizeImageLarge": "The image size is too large, please select another image.",
|
||||||
"SomethingWentWrong": "Something went wrong.",
|
"SomethingWentWrong": "Something went wrong.",
|
||||||
|
"SomethingWentWrongDescription": "Click Send report to automatically generate a report and help us fix the error. None of your personal data will be used in the report",
|
||||||
"SortBy": "Sort by",
|
"SortBy": "Sort by",
|
||||||
"SpacesInLocalPart": "Local part can't contain spaces",
|
"SpacesInLocalPart": "Local part can't contain spaces",
|
||||||
"Standard": "Standard",
|
"Standard": "Standard",
|
||||||
|
@ -176,7 +176,8 @@ public class SettingsController : BaseSettingsController
|
|||||||
StorageBucket = _configuration["firebase:storageBucket"] ?? "",
|
StorageBucket = _configuration["firebase:storageBucket"] ?? "",
|
||||||
MessagingSenderId = _configuration["firebase:messagingSenderId"] ?? "",
|
MessagingSenderId = _configuration["firebase:messagingSenderId"] ?? "",
|
||||||
AppId = _configuration["firebase:appId"] ?? "",
|
AppId = _configuration["firebase:appId"] ?? "",
|
||||||
MeasurementId = _configuration["firebase:measurementId"] ?? ""
|
MeasurementId = _configuration["firebase:measurementId"] ?? "",
|
||||||
|
DataBaseUrl = _configuration["firebase:databaseURL"] ?? ""
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.HelpLink = _commonLinkUtility.GetHelpLink(_settingsManager, _additionalWhiteLabelSettingsHelper, true);
|
settings.HelpLink = _commonLinkUtility.GetHelpLink(_settingsManager, _additionalWhiteLabelSettingsHelper, true);
|
||||||
|
@ -35,5 +35,5 @@ public class FirebaseDto
|
|||||||
public string MessagingSenderId { get; set; }
|
public string MessagingSenderId { get; set; }
|
||||||
public string AppId { get; set; }
|
public string AppId { get; set; }
|
||||||
public string MeasurementId { get; set; }
|
public string MeasurementId { get; set; }
|
||||||
|
public string DataBaseUrl { get; set; }
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ using ASC.Core.Data;
|
|||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
using Constants = ASC.Core.Users.Constants;
|
using Constants = ASC.Core.Users.Constants;
|
||||||
|
using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
|
||||||
|
|
||||||
namespace ASC.Web.Core;
|
namespace ASC.Web.Core;
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ public class CookiesManager
|
|||||||
private readonly CoreBaseSettings _coreBaseSettings;
|
private readonly CoreBaseSettings _coreBaseSettings;
|
||||||
private readonly DbLoginEventsManager _dbLoginEventsManager;
|
private readonly DbLoginEventsManager _dbLoginEventsManager;
|
||||||
private readonly MessageService _messageService;
|
private readonly MessageService _messageService;
|
||||||
|
private readonly SameSiteMode? _sameSiteMode;
|
||||||
|
|
||||||
public CookiesManager(
|
public CookiesManager(
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
@ -61,7 +63,8 @@ public class CookiesManager
|
|||||||
TenantManager tenantManager,
|
TenantManager tenantManager,
|
||||||
CoreBaseSettings coreBaseSettings,
|
CoreBaseSettings coreBaseSettings,
|
||||||
DbLoginEventsManager dbLoginEventsManager,
|
DbLoginEventsManager dbLoginEventsManager,
|
||||||
MessageService messageService)
|
MessageService messageService,
|
||||||
|
IConfiguration configuration)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
@ -71,6 +74,11 @@ public class CookiesManager
|
|||||||
_coreBaseSettings = coreBaseSettings;
|
_coreBaseSettings = coreBaseSettings;
|
||||||
_dbLoginEventsManager = dbLoginEventsManager;
|
_dbLoginEventsManager = dbLoginEventsManager;
|
||||||
_messageService = messageService;
|
_messageService = messageService;
|
||||||
|
|
||||||
|
if (Enum.TryParse<SameSiteMode>(configuration["web:samesite"], out var sameSiteMode))
|
||||||
|
{
|
||||||
|
_sameSiteMode = sameSiteMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCookies(CookiesType type, string value, bool session = false)
|
public void SetCookies(CookiesType type, string value, bool session = false)
|
||||||
@ -89,10 +97,20 @@ public class CookiesManager
|
|||||||
{
|
{
|
||||||
options.HttpOnly = true;
|
options.HttpOnly = true;
|
||||||
|
|
||||||
|
if (_sameSiteMode.HasValue && _sameSiteMode.Value != SameSiteMode.None)
|
||||||
|
{
|
||||||
|
options.SameSite = _sameSiteMode.Value;
|
||||||
|
}
|
||||||
|
|
||||||
var urlRewriter = _httpContextAccessor.HttpContext.Request.Url();
|
var urlRewriter = _httpContextAccessor.HttpContext.Request.Url();
|
||||||
if (urlRewriter.Scheme == "https")
|
if (urlRewriter.Scheme == "https")
|
||||||
{
|
{
|
||||||
options.Secure = true;
|
options.Secure = true;
|
||||||
|
|
||||||
|
if (_sameSiteMode.HasValue && _sameSiteMode.Value == SameSiteMode.None)
|
||||||
|
{
|
||||||
|
options.SameSite = _sameSiteMode.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FromCors())
|
if (FromCors())
|
||||||
|
Loading…
Reference in New Issue
Block a user