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
|
||||
fi
|
||||
|
||||
if command_exists apt-get; then
|
||||
apt-get -y update -qq
|
||||
apt-get -y -q install python3-pip
|
||||
elif command_exists yum; then
|
||||
py3_version=$(python3 -c 'import sys; print(sys.version_info.minor)')
|
||||
if [[ $py3_version -lt 6 ]]; then
|
||||
curl -O https://bootstrap.pypa.io/pip/3.$py3_version/get-pip.py
|
||||
else
|
||||
curl -O https://bootstrap.pypa.io/get-pip.py
|
||||
python3 get-pip.py || true
|
||||
rm get-pip.py
|
||||
fi
|
||||
fi
|
||||
python3 get-pip.py
|
||||
rm get-pip.py
|
||||
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install docker-compose
|
||||
@ -571,21 +571,6 @@ install_docker_compose () {
|
||||
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 () {
|
||||
RESERVED_PORTS=(3306 8092);
|
||||
ARRAY_PORTS=();
|
||||
@ -779,10 +764,6 @@ get_available_version () {
|
||||
install_curl;
|
||||
fi
|
||||
|
||||
if ! command_exists jq ; then
|
||||
install_jq
|
||||
fi
|
||||
|
||||
CREDENTIALS="";
|
||||
AUTH_HEADER="";
|
||||
TAGS_RESP="";
|
||||
@ -953,6 +934,13 @@ download_files () {
|
||||
install_service svn subversion
|
||||
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}
|
||||
|
||||
reconfigure STATUS ${STATUS}
|
||||
@ -965,8 +953,8 @@ download_files () {
|
||||
}
|
||||
|
||||
reconfigure () {
|
||||
local VARIABLE_NAME=$1
|
||||
local VARIABLE_VALUE=$2
|
||||
local VARIABLE_NAME="$1"
|
||||
local VARIABLE_VALUE="$2"
|
||||
|
||||
if [[ -n ${VARIABLE_VALUE} ]]; then
|
||||
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
|
||||
script-not-executable var/www/{{product}}/services/*/node_modules/*
|
||||
# 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
Packager: %{packager}
|
||||
Summary: Backup
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -8,6 +9,7 @@ AutoReqProv: no
|
||||
Backup
|
||||
|
||||
%package common
|
||||
Packager: %{packager}
|
||||
Summary: Common
|
||||
Group: Applications/Internet
|
||||
Requires: logrotate
|
||||
@ -15,6 +17,7 @@ Requires: logrotate
|
||||
Common
|
||||
|
||||
%package files-services
|
||||
Packager: %{packager}
|
||||
Summary: Files-services
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -25,6 +28,7 @@ AutoReqProv: no
|
||||
Files-services
|
||||
|
||||
%package notify
|
||||
Packager: %{packager}
|
||||
Summary: Notify
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -34,6 +38,7 @@ AutoReqProv: no
|
||||
Notify
|
||||
|
||||
%package files
|
||||
Packager: %{packager}
|
||||
Summary: Files
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -43,6 +48,7 @@ AutoReqProv: no
|
||||
Files
|
||||
|
||||
%package proxy
|
||||
Packager: %{packager}
|
||||
Summary: Proxy
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -53,6 +59,7 @@ AutoReqProv: no
|
||||
Proxy
|
||||
|
||||
%package studio-notify
|
||||
Packager: %{packager}
|
||||
Summary: Studio-notify
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -62,6 +69,7 @@ AutoReqProv: no
|
||||
Studio-notify
|
||||
|
||||
%package people-server
|
||||
Packager: %{packager}
|
||||
Summary: People-server
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -71,6 +79,7 @@ AutoReqProv: no
|
||||
People-server
|
||||
|
||||
%package socket
|
||||
Packager: %{packager}
|
||||
Summary: Socket
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -80,6 +89,7 @@ AutoReqProv: no
|
||||
Socket
|
||||
|
||||
%package studio
|
||||
Packager: %{packager}
|
||||
Summary: Studio
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -89,6 +99,7 @@ AutoReqProv: no
|
||||
Studio
|
||||
|
||||
%package api
|
||||
Packager: %{packager}
|
||||
Summary: Api
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -98,6 +109,7 @@ AutoReqProv: no
|
||||
Api
|
||||
|
||||
%package api-system
|
||||
Packager: %{packager}
|
||||
Summary: Api-system
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -107,6 +119,7 @@ AutoReqProv: no
|
||||
Api-system
|
||||
|
||||
%package ssoauth
|
||||
Packager: %{packager}
|
||||
Summary: Ssoauth
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -116,6 +129,7 @@ AutoReqProv: no
|
||||
Ssoauth
|
||||
|
||||
%package clear-events
|
||||
Packager: %{packager}
|
||||
Summary: Clear-events
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -125,6 +139,7 @@ AutoReqProv: no
|
||||
Clear-events
|
||||
|
||||
%package backup-background
|
||||
Packager: %{packager}
|
||||
Summary: Backup-background
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -134,6 +149,7 @@ AutoReqProv: no
|
||||
Backup-background
|
||||
|
||||
%package radicale
|
||||
Packager: %{packager}
|
||||
Summary: Radicale
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -143,6 +159,7 @@ AutoReqProv: no
|
||||
Radicale
|
||||
|
||||
%package doceditor
|
||||
Packager: %{packager}
|
||||
Summary: Doceditor
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -152,6 +169,7 @@ AutoReqProv: no
|
||||
Doceditor
|
||||
|
||||
%package migration-runner
|
||||
Packager: %{packager}
|
||||
Summary: Migration-runner
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -161,6 +179,7 @@ AutoReqProv: no
|
||||
Migration-runner
|
||||
|
||||
%package login
|
||||
Packager: %{packager}
|
||||
Summary: Login
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
@ -170,6 +189,7 @@ AutoReqProv: no
|
||||
Login
|
||||
|
||||
%package healthchecks
|
||||
Packager: %{packager}
|
||||
Summary: Healthchecks
|
||||
Group: Applications/Internet
|
||||
Requires: %name-common = %version-%release
|
||||
|
@ -14,6 +14,7 @@ AutoReqProv: no
|
||||
|
||||
URL: http://onlyoffice.com
|
||||
Vendor: Ascensio System SIA
|
||||
Packager: %{packager}
|
||||
License: AGPLv3
|
||||
|
||||
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 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();
|
||||
subDirs.Reverse();
|
||||
|
@ -457,7 +457,15 @@ public class GoogleCloudStorage : BaseStorage
|
||||
await foreach (var obj in objToDel)
|
||||
{
|
||||
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 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);
|
||||
}
|
||||
|
||||
private bool EnableQuotaCheck(string domain)
|
||||
{
|
||||
return (QuotaController != null) && !domain.EndsWith("_temp");
|
||||
}
|
||||
private bool EnableQuotaCheck(string domain)
|
||||
{
|
||||
return (QuotaController != null) && !domain.EndsWith("_temp");
|
||||
}
|
||||
|
||||
public async Task<Uri> SaveAsync(string domain, string path, Stream stream, string contentType,
|
||||
string contentDisposition, ACL acl, string contentEncoding = null, int cacheDays = 5,
|
||||
@ -200,7 +200,7 @@ public class RackspaceCloudStorage : BaseStorage
|
||||
{
|
||||
var buffered = _tempStream.GetBuffered(stream);
|
||||
|
||||
if (EnableQuotaCheck(domain))
|
||||
if (EnableQuotaCheck(domain))
|
||||
{
|
||||
QuotaController.QuotaUsedCheck(buffered.Length);
|
||||
}
|
||||
@ -482,7 +482,15 @@ public class RackspaceCloudStorage : BaseStorage
|
||||
foreach (var obj in objToDel)
|
||||
{
|
||||
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 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) + '"';
|
||||
|
||||
|
@ -495,7 +495,14 @@ public class S3Storage : BaseStorage
|
||||
|
||||
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 Lazy<long> _lazyCurrentSize;
|
||||
private long _currentSize;
|
||||
public string ExcludePattern { get; set; }
|
||||
|
||||
public TenantQuotaController(TenantManager tenantManager, AuthContext authContext, TenantQuotaFeatureChecker<MaxFileSizeFeature, long> maxFileSizeChecker, TenantQuotaFeatureChecker<MaxTotalSizeFeature, long> maxTotalSizeChecker)
|
||||
{
|
||||
@ -58,13 +59,14 @@ public class TenantQuotaController : IQuotaController
|
||||
_maxTotalSizeChecker = maxTotalSizeChecker;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
public void Init(int tenant)
|
||||
|
||||
public void Init(int tenant, string excludePattern = null)
|
||||
{
|
||||
_tenant = tenant;
|
||||
_lazyCurrentSize = new Lazy<long>(() => _tenantManager.FindTenantQuotaRows(tenant)
|
||||
.Where(r => UsedInQuota(r.Tag))
|
||||
.Sum(r => r.Counter));
|
||||
ExcludePattern = excludePattern;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@
|
||||
"oidc": {
|
||||
"authority": ""
|
||||
},
|
||||
"server-root": ""
|
||||
"server-root": ""
|
||||
},
|
||||
"license": {
|
||||
"file": {
|
||||
@ -296,7 +296,8 @@
|
||||
"book-training-email": "training@onlyoffice.com",
|
||||
"documentation-email": "documentation@onlyoffice.com",
|
||||
"max-upload-size": 5242880,
|
||||
"zendesk-key": ""
|
||||
"zendesk-key": "",
|
||||
"samesite": ""
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
@ -342,7 +343,8 @@
|
||||
"storageBucket": "",
|
||||
"messagingSenderId": "",
|
||||
"appId": "",
|
||||
"measurementId": ""
|
||||
"measurementId": "",
|
||||
"databaseURL": ""
|
||||
},
|
||||
"debug-info": {
|
||||
"enabled": "true"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docspace",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@docspace/client",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"homepage": "",
|
||||
"scripts": {
|
||||
|
@ -487,6 +487,7 @@ const Shell = ({ items = [], page = "home", ...rest }) => {
|
||||
"/files/trash/filter",
|
||||
|
||||
"/accounts",
|
||||
"/accounts/changeOwner",
|
||||
"/accounts/filter",
|
||||
|
||||
"/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 CreateRoomConfirmDialog from "./CreateRoomConfirmDialog";
|
||||
import PortalRenamingDialog from "./PortalRenamingDialog";
|
||||
import ReportDialog from "./ReportDialog";
|
||||
|
||||
export {
|
||||
EmptyTrashDialog,
|
||||
@ -62,4 +63,5 @@ export {
|
||||
InviteUsersWarningDialog,
|
||||
LogoutAllConnectionDialog,
|
||||
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
|
||||
exact
|
||||
withManager
|
||||
withManager
|
||||
path={["/accounts"]}
|
||||
component={HomeRedirectToFilter}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
withManager
|
||||
path={["/accounts/changeOwner"]}
|
||||
component={RedirectWithChangeOwnerDialog}
|
||||
/>
|
||||
<PrivateRoute
|
||||
path={"/accounts/filter"}
|
||||
withManager
|
||||
@ -55,6 +61,20 @@ const HomeRedirectToFilter = (props) => {
|
||||
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 { loadBaseInfo, isLoading, setFirstLoad } = props;
|
||||
|
||||
|
@ -44,6 +44,8 @@ const PureHome = ({
|
||||
withPaging,
|
||||
onClickBack,
|
||||
setPortalTariff,
|
||||
|
||||
setChangeOwnerDialogVisible,
|
||||
}) => {
|
||||
const { location } = history;
|
||||
const { pathname } = location;
|
||||
@ -73,6 +75,9 @@ const PureHome = ({
|
||||
setIsLoading(false);
|
||||
setIsRefresh(false);
|
||||
});
|
||||
|
||||
if (location?.state?.openChangeOwnerDialog)
|
||||
setChangeOwnerDialogVisible(true);
|
||||
}
|
||||
}, [pathname, location, setSelectedNode]);
|
||||
|
||||
@ -140,24 +145,15 @@ export default inject(
|
||||
const { settingsStore, currentTariffStatusStore } = auth;
|
||||
const { setPortalTariff } = currentTariffStatusStore;
|
||||
const { showCatalog, withPaging } = settingsStore;
|
||||
const {
|
||||
usersStore,
|
||||
selectedGroupStore,
|
||||
loadingStore,
|
||||
viewAs,
|
||||
} = peopleStore;
|
||||
const { usersStore, selectedGroupStore, loadingStore, viewAs } =
|
||||
peopleStore;
|
||||
const { getUsersList } = usersStore;
|
||||
const { selectedGroup } = selectedGroupStore;
|
||||
const { setSelectedNode } = treeFoldersStore;
|
||||
const { onClickBack } = filesActionsStore;
|
||||
const {
|
||||
isLoading,
|
||||
setIsLoading,
|
||||
setIsRefresh,
|
||||
firstLoad,
|
||||
setFirstLoad,
|
||||
} = loadingStore;
|
||||
|
||||
const { isLoading, setIsLoading, setIsRefresh, firstLoad, setFirstLoad } =
|
||||
loadingStore;
|
||||
const { setChangeOwnerDialogVisible } = peopleStore.dialogStore;
|
||||
return {
|
||||
setPortalTariff,
|
||||
isAdmin: auth.isAdmin,
|
||||
@ -176,6 +172,7 @@ export default inject(
|
||||
snackbarExist: auth.settingsStore.snackbarExist,
|
||||
withPaging,
|
||||
onClickBack,
|
||||
setChangeOwnerDialogVisible,
|
||||
};
|
||||
}
|
||||
)(observer(withRouter(Home)));
|
||||
|
@ -1,23 +1,95 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import ErrorContainer from "@docspace/common/components/ErrorContainer";
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import { inject, observer } from "mobx-react";
|
||||
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";
|
||||
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 { 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 (
|
||||
<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 = {
|
||||
match: PropTypes.object,
|
||||
};
|
||||
const Error520Wrapper = inject(({ auth }) => {
|
||||
const { currentColorScheme, firebaseHelper } = auth.settingsStore;
|
||||
|
||||
export default () => (
|
||||
return {
|
||||
currentColorScheme,
|
||||
FirebaseHelper: firebaseHelper,
|
||||
};
|
||||
})(observer(Error520));
|
||||
|
||||
export default (props) => (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Error520 />
|
||||
<Error520Wrapper {...props} />
|
||||
</I18nextProvider>
|
||||
);
|
||||
|
@ -132,7 +132,12 @@ const FilesSection = React.memo(({ withMainButton }) => {
|
||||
exact
|
||||
restricted
|
||||
withManager
|
||||
path={["/accounts", "/accounts/filter", "/accounts/create/:type"]}
|
||||
path={[
|
||||
"/accounts",
|
||||
"/accounts/changeOwner",
|
||||
"/accounts/filter",
|
||||
"/accounts/create/:type",
|
||||
]}
|
||||
component={Accounts}
|
||||
/>
|
||||
|
||||
|
@ -90,11 +90,11 @@ const PersonalSettings = ({
|
||||
hideAdminSettings={!showAdminSettings}
|
||||
>
|
||||
<Box className="settings-section">
|
||||
{showTitle && (
|
||||
{/* {showTitle && (
|
||||
<Heading className="heading" level={2} size="xsmall">
|
||||
{t("Common:Common")}
|
||||
</Heading>
|
||||
)}
|
||||
)} */}
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
label={thumbnailsSizeLabel}
|
||||
@ -124,6 +124,14 @@ const PersonalSettings = ({
|
||||
isChecked={confirmDelete}
|
||||
/>
|
||||
)}
|
||||
{!isVisitor && (
|
||||
<ToggleButton
|
||||
className="toggle-btn"
|
||||
label={t("UpdateOrCreate")}
|
||||
onChange={onChangeUpdateIfExist}
|
||||
isChecked={updateIfExist}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* <Box className="settings-section">
|
||||
@ -154,7 +162,7 @@ const PersonalSettings = ({
|
||||
/>
|
||||
</Box> */}
|
||||
|
||||
{!isVisitor && (
|
||||
{/* {!isVisitor && (
|
||||
<Box className="settings-section">
|
||||
<Heading className="heading" level={2} size="xsmall">
|
||||
{t("StoringFileVersion")}
|
||||
@ -176,7 +184,7 @@ const PersonalSettings = ({
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
)} */}
|
||||
</StyledSettings>
|
||||
);
|
||||
};
|
||||
|
@ -63,25 +63,25 @@ const SectionBodyContent = ({ isErrorSettings, history, user }) => {
|
||||
[setting, history]
|
||||
);
|
||||
|
||||
const showAdminSettings = user.isAdmin || user.isOwner;
|
||||
//const showAdminSettings = user.isAdmin || user.isOwner;
|
||||
|
||||
return isErrorSettings ? (
|
||||
<Error520 />
|
||||
) : (
|
||||
<StyledContainer>
|
||||
{!showAdminSettings ? (
|
||||
<PersonalSettings
|
||||
t={t}
|
||||
showTitle={true}
|
||||
showAdminSettings={showAdminSettings}
|
||||
/>
|
||||
) : (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={setting === "common" ? commonSettings : adminSettings}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
)}
|
||||
{/* {!showAdminSettings ? ( */}
|
||||
<PersonalSettings
|
||||
t={t}
|
||||
showTitle={true}
|
||||
showAdminSettings={false} //showAdminSettings
|
||||
/>
|
||||
{/* ) : (
|
||||
<Submenu
|
||||
data={data}
|
||||
startSelect={setting === "common" ? commonSettings : adminSettings}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
)} */}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
@ -261,6 +261,10 @@ const Wizard = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onClickRetry = () => {
|
||||
window.location.href = "/";
|
||||
};
|
||||
|
||||
if (!isWizardLoaded)
|
||||
return <Loader className="pageLoader" type="rombs" size="40px" />;
|
||||
|
||||
@ -270,7 +274,7 @@ const Wizard = (props) => {
|
||||
headerText={t("Common:SomethingWentWrong")}
|
||||
bodyText={t("ErrorInitWizard")}
|
||||
buttonText={t("ErrorInitWizardButton")}
|
||||
buttonUrl="/"
|
||||
onClickButton={onClickRetry}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -2163,6 +2163,8 @@ class FilesActionStore {
|
||||
id = urlFilter.folder;
|
||||
}
|
||||
|
||||
if (id === undefined) return;
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
fetchFiles(id, null, true, false).finally(() => setIsLoading(false));
|
||||
|
@ -5,13 +5,13 @@ import Error520 from "client/Error520";
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
this.state = { error: null };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
static getDerivedStateFromError(error) {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
return { hasError: true };
|
||||
return { error: error ?? "Unhandled exception" };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
@ -21,9 +21,9 @@ class ErrorBoundary extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
if (this.state.error) {
|
||||
// You can render any custom fallback UI
|
||||
return <Error520 />;
|
||||
return <Error520 errorLog={this.state.error} />;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
|
@ -12,9 +12,10 @@ const ErrorContainer = (props) => {
|
||||
headerText,
|
||||
bodyText,
|
||||
buttonText,
|
||||
buttonUrl,
|
||||
onClickButton,
|
||||
children,
|
||||
customizedBodyText,
|
||||
isPrimaryButton,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -354,16 +355,16 @@ const ErrorContainer = (props) => {
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{buttonText && buttonUrl && (
|
||||
{buttonText && onClickButton && (
|
||||
<div id="button-container">
|
||||
<Button
|
||||
theme={rest?.theme}
|
||||
id="button"
|
||||
size="normal"
|
||||
scale
|
||||
primary
|
||||
primary={isPrimaryButton}
|
||||
label={buttonText}
|
||||
onClick={() => (window.location.href = buttonUrl)}
|
||||
onClick={onClickButton}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -372,11 +373,16 @@ const ErrorContainer = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
ErrorContainer.defaultProps = {
|
||||
isPrimaryButton: true,
|
||||
};
|
||||
|
||||
ErrorContainer.propTypes = {
|
||||
headerText: PropTypes.string,
|
||||
bodyText: PropTypes.string,
|
||||
isPrimaryButton: PropTypes.bool,
|
||||
buttonText: PropTypes.string,
|
||||
buttonUrl: PropTypes.string,
|
||||
onClickButton: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,6 @@ storiesOf("Components| ErrorContainer", module)
|
||||
"This page was removed, renamed or doesn’t exist anymore."
|
||||
)}
|
||||
buttonText={text("buttonText", "Return to homepage")}
|
||||
buttonUrl={text("buttonUrl", "/")}
|
||||
onClickButton={() => console.log("click")}
|
||||
/>
|
||||
));
|
||||
|
@ -15,7 +15,6 @@ describe("<ErrorContainer />", () => {
|
||||
headerText="Some error has happened"
|
||||
bodyText="Try again later"
|
||||
buttonText="Go back"
|
||||
buttonUrl="/page"
|
||||
/>
|
||||
);
|
||||
|
||||
@ -23,7 +22,6 @@ describe("<ErrorContainer />", () => {
|
||||
expect(wrapper.prop("headerText")).toEqual("Some error has happened");
|
||||
expect(wrapper.prop("bodyText")).toEqual("Try again later");
|
||||
expect(wrapper.prop("buttonText")).toEqual("Go back");
|
||||
expect(wrapper.prop("buttonUrl")).toEqual("/page");
|
||||
});
|
||||
|
||||
it("accepts id", () => {
|
||||
|
@ -10,7 +10,7 @@ const StyledErrorContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
margin: 0 16px;
|
||||
padding-top: 36px;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
@ -44,16 +44,14 @@ const StyledErrorContainer = styled.div`
|
||||
}
|
||||
|
||||
#button-container {
|
||||
margin: 0 0 auto 0;
|
||||
width: 320px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
#button {
|
||||
display: inline-block;
|
||||
margin: 0 0 36px 0;
|
||||
}
|
||||
#customized-text {
|
||||
color: ${(props) => props.theme.errorContainer.bodyText};
|
||||
}
|
||||
|
||||
@media screen and (max-width: 960px) {
|
||||
body {
|
||||
padding: 24px 24px 0 24px;
|
||||
@ -62,10 +60,6 @@ const StyledErrorContainer = styled.div`
|
||||
#container {
|
||||
margin: 12px 0 48px 0;
|
||||
}
|
||||
|
||||
#button {
|
||||
margin: 0 0 24px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 620px) {
|
||||
@ -88,13 +82,11 @@ const StyledErrorContainer = styled.div`
|
||||
}
|
||||
|
||||
#button-container {
|
||||
align-self: stretch;
|
||||
margin: auto 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#button {
|
||||
width: 100%;
|
||||
margin: 0 0 18px 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@docspace/common",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "echo 'skip it'",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import firebase from "firebase/app";
|
||||
import "firebase/remote-config";
|
||||
import "firebase/storage";
|
||||
import "firebase/database";
|
||||
|
||||
import CampaignsCloudPngUrl from "PUBLIC_DIR/images/campaigns.cloud.png";
|
||||
import CampaignsDesktopPngUrl from "PUBLIC_DIR/images/campaigns.desktop.png";
|
||||
@ -12,6 +13,7 @@ class FirebaseHelper {
|
||||
remoteConfig = null;
|
||||
firebaseConfig = null;
|
||||
firebaseStorage = null;
|
||||
firebaseDB = null;
|
||||
|
||||
constructor(settings) {
|
||||
this.firebaseConfig = settings;
|
||||
@ -28,6 +30,8 @@ class FirebaseHelper {
|
||||
|
||||
this.remoteConfig = firebase.remoteConfig();
|
||||
|
||||
this.firebaseDB = firebase.database();
|
||||
|
||||
this.remoteConfig.settings = {
|
||||
fetchTimeoutMillis: 3600000,
|
||||
minimumFetchIntervalMillis: 3600000,
|
||||
@ -64,6 +68,10 @@ class FirebaseHelper {
|
||||
);
|
||||
}
|
||||
|
||||
get isEnabledDB() {
|
||||
return this.isEnabled && this.config["databaseUrl"];
|
||||
}
|
||||
|
||||
async checkMaintenance() {
|
||||
if (!this.isEnabled) return Promise.reject("Not enabled");
|
||||
|
||||
@ -145,6 +153,16 @@ class FirebaseHelper {
|
||||
const domain = this.config["authDomain"];
|
||||
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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@docspace/components",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "echo 'skip it'",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@docspace/editor",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"homepage": "/doceditor",
|
||||
"scripts": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@docspace/login",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"homepage": "/login",
|
||||
"scripts": {
|
||||
|
@ -46,6 +46,8 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ThumbnailSettings _thumbnailSettings;
|
||||
private readonly IQuotaService _quotaService;
|
||||
private readonly StorageFactory _storageFactory;
|
||||
private readonly TenantQuotaController _tenantQuotaController;
|
||||
|
||||
public FileDao(
|
||||
ILogger<FileDao> logger,
|
||||
@ -73,7 +75,9 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
Settings settings,
|
||||
IMapper mapper,
|
||||
ThumbnailSettings thumbnailSettings,
|
||||
IQuotaService quotaService)
|
||||
IQuotaService quotaService,
|
||||
StorageFactory storageFactory,
|
||||
TenantQuotaController tenantQuotaController)
|
||||
: base(
|
||||
dbContextManager,
|
||||
userManager,
|
||||
@ -102,6 +106,8 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
_mapper = mapper;
|
||||
_thumbnailSettings = thumbnailSettings;
|
||||
_quotaService = quotaService;
|
||||
_storageFactory = storageFactory;
|
||||
_tenantQuotaController = tenantQuotaController;
|
||||
}
|
||||
|
||||
public Task InvalidateCacheAsync(int fileId)
|
||||
@ -559,7 +565,7 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
}
|
||||
else
|
||||
{
|
||||
if(uploadSession != null)
|
||||
if (uploadSession != null)
|
||||
{
|
||||
await _chunkedUploadSessionHolder.MoveAsync(uploadSession, GetUniqFilePath(file));
|
||||
}
|
||||
@ -795,7 +801,10 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
|
||||
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)
|
||||
@ -919,7 +928,7 @@ internal class FileDao : AbstractDao, IFileDao<int>
|
||||
.OrderByDescending(r => r.Level)
|
||||
.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)
|
||||
{
|
||||
return _globalStore.GetStore().IsFileAsync(string.Empty, GetUniqFilePath(file));
|
||||
|
@ -104,6 +104,9 @@
|
||||
"EnterName": "Enter name",
|
||||
"Error": "Error",
|
||||
"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",
|
||||
"FeedbackAndSupport": "Feedback & Support",
|
||||
"FillFormButton": "Fill in the form",
|
||||
@ -198,6 +201,7 @@
|
||||
"RecoverDescribeYourProblemPlaceholder": "Describe your problem",
|
||||
"RecoverTitle": "Access recovery",
|
||||
"RegistrationEmail": "Your registration email address",
|
||||
"ReloadPage": "Reload page",
|
||||
"Rename": "Rename",
|
||||
"RepeatInvitation": "Repeat invitation",
|
||||
"RequiredField": "Required field",
|
||||
@ -220,8 +224,9 @@
|
||||
"SelectDOCXFormat": "Select .DOCX file",
|
||||
"SelectFile": "Select file",
|
||||
"SendButton": "Send",
|
||||
"Sending": "Sending...",
|
||||
"SendReport": "Send report",
|
||||
"SendRequest": "Send request",
|
||||
"Sending": "Sending...",
|
||||
"Sessions": "Sessions",
|
||||
"Settings": "Settings",
|
||||
"SettingsDocSpace": "DocSpace Settings",
|
||||
@ -237,6 +242,7 @@
|
||||
"Size": "Size",
|
||||
"SizeImageLarge": "The image size is too large, please select another image.",
|
||||
"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",
|
||||
"SpacesInLocalPart": "Local part can't contain spaces",
|
||||
"Standard": "Standard",
|
||||
|
@ -176,7 +176,8 @@ public class SettingsController : BaseSettingsController
|
||||
StorageBucket = _configuration["firebase:storageBucket"] ?? "",
|
||||
MessagingSenderId = _configuration["firebase:messagingSenderId"] ?? "",
|
||||
AppId = _configuration["firebase:appId"] ?? "",
|
||||
MeasurementId = _configuration["firebase:measurementId"] ?? ""
|
||||
MeasurementId = _configuration["firebase:measurementId"] ?? "",
|
||||
DataBaseUrl = _configuration["firebase:databaseURL"] ?? ""
|
||||
};
|
||||
|
||||
settings.HelpLink = _commonLinkUtility.GetHelpLink(_settingsManager, _additionalWhiteLabelSettingsHelper, true);
|
||||
|
@ -35,5 +35,5 @@ public class FirebaseDto
|
||||
public string MessagingSenderId { get; set; }
|
||||
public string AppId { 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 Constants = ASC.Core.Users.Constants;
|
||||
using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
|
||||
|
||||
namespace ASC.Web.Core;
|
||||
|
||||
@ -52,6 +53,7 @@ public class CookiesManager
|
||||
private readonly CoreBaseSettings _coreBaseSettings;
|
||||
private readonly DbLoginEventsManager _dbLoginEventsManager;
|
||||
private readonly MessageService _messageService;
|
||||
private readonly SameSiteMode? _sameSiteMode;
|
||||
|
||||
public CookiesManager(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
@ -61,7 +63,8 @@ public class CookiesManager
|
||||
TenantManager tenantManager,
|
||||
CoreBaseSettings coreBaseSettings,
|
||||
DbLoginEventsManager dbLoginEventsManager,
|
||||
MessageService messageService)
|
||||
MessageService messageService,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_userManager = userManager;
|
||||
@ -71,6 +74,11 @@ public class CookiesManager
|
||||
_coreBaseSettings = coreBaseSettings;
|
||||
_dbLoginEventsManager = dbLoginEventsManager;
|
||||
_messageService = messageService;
|
||||
|
||||
if (Enum.TryParse<SameSiteMode>(configuration["web:samesite"], out var sameSiteMode))
|
||||
{
|
||||
_sameSiteMode = sameSiteMode;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCookies(CookiesType type, string value, bool session = false)
|
||||
@ -89,10 +97,20 @@ public class CookiesManager
|
||||
{
|
||||
options.HttpOnly = true;
|
||||
|
||||
if (_sameSiteMode.HasValue && _sameSiteMode.Value != SameSiteMode.None)
|
||||
{
|
||||
options.SameSite = _sameSiteMode.Value;
|
||||
}
|
||||
|
||||
var urlRewriter = _httpContextAccessor.HttpContext.Request.Url();
|
||||
if (urlRewriter.Scheme == "https")
|
||||
{
|
||||
options.Secure = true;
|
||||
|
||||
if (_sameSiteMode.HasValue && _sameSiteMode.Value == SameSiteMode.None)
|
||||
{
|
||||
options.SameSite = _sameSiteMode.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (FromCors())
|
||||
|
Loading…
Reference in New Issue
Block a user