Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
Binary file not shown.
@@ -0,0 +1,9 @@
app_registry:
mimetypes:
- mime_type: application/vnd.oasis.opendocument.text
extension: odt
name: OpenDocument
description: OpenDocument text document
icon: ""
default_app: FakeOffice
allow_creation: true
@@ -0,0 +1,5 @@
password
12345678
123
КуСфера
КуСфера-1
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
#
# $1 - root path where .bingo resides
# $2 - name of the cache item
#
ROOT_PATH="$1"
if [ -z "$1" ]; then
ROOT_PATH="/drone/src"
fi
BINGO_DIR="$ROOT_PATH/.bingo"
# generate hash of a .bingo folder
BINGO_HASH=$(cat "$BINGO_DIR"/* | sha256sum | cut -d ' ' -f 1)
URL="$CACHE_ENDPOINT/$CACHE_BUCKET/qsfera/go-bin/$BINGO_HASH/$2"
mc alias set s3 "$MC_HOST" "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY"
if mc ls --json s3/"$CACHE_BUCKET"/qsfera/go-bin/"$BINGO_HASH"/$2 | grep "\"status\":\"success\""; then
echo "[INFO] Go bin cache with has '$BINGO_HASH' exists."
ENV="BIN_CACHE_FOUND=true\n"
else
# stored hash of a .bingo folder to '.bingo_hash' file
echo "$BINGO_HASH" >"$ROOT_PATH/.bingo_hash"
echo "[INFO] Go bin cache with has '$BINGO_HASH' does not exist."
ENV="BIN_CACHE_FOUND=false\n"
fi
echo -e $ENV >> .env
@@ -0,0 +1,24 @@
#!/bin/bash
source .woodpecker.env
# if no $1 is supplied end the script
# Can be web, acceptance or e2e
if [ -z "$1" ]; then
echo "No cache item is supplied."
exit 1
fi
echo "Checking web version - $WEB_COMMITID in cache"
mc alias set s3 "$MC_HOST" "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY"
if mc ls --json s3/"$CACHE_BUCKET"/qsfera/web-test-runner/"$WEB_COMMITID"/"$1".tar.gz | grep "\"status\":\"success\"";
then
echo "$1 cache with commit id $WEB_COMMITID already available."
ENV="WEB_CACHE_FOUND=true\n"
else
echo "$1 cache with commit id $WEB_COMMITID was not available."
ENV="WEB_CACHE_FOUND=false\n"
fi
echo -e $ENV >> .woodpecker.env
@@ -0,0 +1,102 @@
const fs = require("fs");
const CI_REPO_NAME = process.env.CI_REPO_NAME;
const CI_COMMIT_SHA = process.env.CI_COMMIT_SHA;
const CI_WORKFLOW_NAME = process.env.CI_WORKFLOW_NAME;
const CI_PIPELINE_EVENT = process.env.CI_PIPELINE_EVENT;
const qsferaBuildWorkflow = "build-qsfera-for-testing";
const webCacheWorkflows = ["cache-web", "cache-web-pnpm", "cache-browsers"];
const INFO_URL = `https://s3.ci.qsfera.eu/public/${CI_REPO_NAME}/pipelines/${CI_COMMIT_SHA}-${CI_PIPELINE_EVENT}/pipeline_info.json`;
function getWorkflowNames(workflows) {
const allWorkflows = [];
for (const workflow of workflows) {
allWorkflows.push(workflow.name);
}
return allWorkflows;
}
function getFailedWorkflows(workflows) {
const failedWorkflows = [];
for (const workflow of workflows) {
if (workflow.state !== "success") {
failedWorkflows.push(workflow.name);
}
}
return failedWorkflows;
}
function hasFailingTestWorkflow(failedWorkflows) {
for (const workflowName of failedWorkflows) {
if (workflowName.startsWith("test-")) {
return true;
}
}
return false;
}
function hasFailingE2eTestWorkflow(failedWorkflows) {
for (const workflowName of failedWorkflows) {
if (workflowName.startsWith("test-e2e-")) {
return true;
}
}
return false;
}
async function main() {
const infoResponse = await fetch(INFO_URL);
if (infoResponse.status === 404) {
console.log("[INFO] No matching previous pipeline found. Continue...");
process.exit(0);
} else if (!infoResponse.ok) {
console.error(
"[ERROR] Failed to fetch previous pipeline info:" +
`\n URL: ${INFO_URL}\n Status: ${infoResponse.status}`
);
process.exit(1);
}
const info = await infoResponse.json();
console.log(info);
if (info.status === "success") {
console.log(
"[INFO] All workflows passed in previous pipeline. Full restart. Continue..."
);
process.exit(0);
}
const allWorkflows = getWorkflowNames(info.workflows);
const failedWorkflows = getFailedWorkflows(info.workflows);
// NOTE: implement for test pipelines only for now
// // run the build workflow if any test workflow has failed
// if (
// CI_WORKFLOW_NAME === qsferaBuildWorkflow &&
// hasFailingTestWorkflow(failedWorkflows)
// ) {
// process.exit(0);
// }
// // run the web cache workflows if any e2e test workflow has failed
// if (
// webCacheWorkflows.includes(CI_WORKFLOW_NAME) &&
// hasFailingE2eTestWorkflow(failedWorkflows)
// ) {
// process.exit(0);
// }
if (!allWorkflows.includes(CI_WORKFLOW_NAME)) {
process.exit(0);
}
if (!failedWorkflows.includes(CI_WORKFLOW_NAME)) {
console.log("[INFO] Workflow passed in previous pipeline. Skip...");
fs.appendFileSync(".woodpecker.env", "SKIP_WORKFLOW=true\n");
process.exit(0);
}
console.log("[INFO] Restarting previously failed workflow. Continue...");
}
main();
@@ -0,0 +1,3 @@
{
"defaultFont": "NotoSans.ttf"
}
@@ -0,0 +1,12 @@
<wopi-discovery>
<net-zone name="external-http">
<app favIconUrl="https://fakeoffice.qsfera.test/favicon.ico" name="wopitest">
<action default="true" ext="wopitest" name="view" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
<action default="true" ext="wopitest" name="edit" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
<action default="true" ext="odt" name="view" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
<action default="true" ext="odt" name="edit" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
<action default="true" ext="txt" name="view" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
<action default="true" ext="txt" name="edit" urlsrc="https://fakeoffice.qsfera.test/not/relevant?"/>
</app>
</net-zone>
</wopi-discovery>
@@ -0,0 +1,25 @@
dn: dc=qsfera,dc=eu
objectClass: organization
objectClass: dcObject
dc: qsfera
o: qsfera
dn: ou=users,dc=qsfera,dc=eu
objectClass: organizationalUnit
ou: users
dn: cn=admin,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: person
cn: admin
sn: admin
uid: ldapadmin
departmentNumber: tenant-1
dn: ou=groups,dc=qsfera,dc=eu
objectClass: organizationalUnit
ou: groups
dn: ou=custom,ou=groups,dc=qsfera,dc=eu
objectClass: organizationalUnit
ou: custom
@@ -0,0 +1,74 @@
dn: uid=alice,ou=users,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: alice
givenName: Alice
sn: Hansen
cn: alice
displayName: Alice Hansen
description: Senior DevOps engineer responsible for cloud infrastructure automation.
mail: alice@example.org
departmentNumber: tenant-1
userPassword:: e1NTSEF9eGY5OXlPOXVxSVJ5eTFxdFhQYVYxTnd6WE4wUWRReVU=
dn: uid=brian,ou=users,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: brian
givenName: Brian
sn: Murphy
cn: brian
displayName: Brian Murphy
description: IT support specialist focused on end-user systems and service desk operations.
mail: brian@example.org
departmentNumber: tenant-1
userPassword:: e1NTSEF9Y0xpdnEzdUxzNzB3SjU0R1dmR0EybndxbUZoRmNoOXQ=
dn: uid=carol,ou=users,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: carol
givenName: Carol
sn: King
cn: carol
displayName: Carol King
description: Project manager leading enterprise IT solutions and digital transformation projects.
mail: carol@example.org
departmentNumber: tenant-2
userPassword:: e1NTSEF9bWFiT2FyNEE4UWlJUm1Pb2JhNm1pYm1QMjQraDkzSEw=
dn: uid=david,ou=users,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: david
givenName: David
sn: Lopez
cn: david
displayName: David Lopez
description: Systems architect working on scalable backend services and platform reliability.
mail: david@example.org
departmentNumber: tenant-2
userPassword:: e1NTSEF9MEh2a3J0UTVONmZNSUhyL1NlWVQvclBYTjg0bi9SYlc=
dn: uid=admin,ou=users,dc=qsfera,dc=eu
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
uid: admin
givenName: Admin
sn: Admin
cn: Admin
displayName: Admin
description: System administrator
mail: admin@example.org
departmentNumber: tenant-1
userPassword:: e1NTSEF9bFU2dDRHSC9Cb28wV2lnM1A0SVAzQTIyWE9aL2pCa1M=
@@ -0,0 +1,13 @@
dn: cn=new-features-lovers,ou=groups,dc=qsfera,dc=eu
objectClass: groupOfNames
objectClass: top
cn: new-features-lovers
description: New features lovers
member: uid=alice,ou=users,dc=qsfera,dc=eu
dn: cn=release-lovers,ou=groups,dc=qsfera,dc=eu
objectClass: groupOfNames
objectClass: top
cn: release-lovers
description: Release lovers
member: uid=david,ou=users,dc=qsfera,dc=eu
@@ -0,0 +1,42 @@
#!/bin/bash
printenv
if [ ! -f /opt/bitnami/openldap/share/openldap.key ]
then
openssl req -x509 -newkey rsa:4096 -keyout /opt/bitnami/openldap/share/openldap.key -out /opt/bitnami/openldap/share/openldap.crt -sha256 -days 365 -batch -nodes
fi
mkdir -p /opt/bitnami/openldap/ldifs
if [ -d "/tmp/ldif-files" ]; then
cp /tmp/ldif-files/*.ldif /opt/bitnami/openldap/ldifs/
fi
/opt/bitnami/scripts/openldap/entrypoint.sh "$@" &
ENTRYPOINT_PID=$!
echo "Waiting for LDAP server to start..."
while ! ldapsearch -x -H ldap://localhost:1389 -D "cn=admin,dc=qsfera,dc=eu" -w admin -b "dc=qsfera,dc=eu" > /dev/null 2>&1; do
sleep 2
done
echo "LDAP server is running, importing LDIF files..."
if [ -f "/opt/bitnami/openldap/ldifs/10_base.ldif" ]; then
echo "Importing 10_base.ldif..."
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=qsfera,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/10_base.ldif
fi
if [ -f "/opt/bitnami/openldap/ldifs/20_users.ldif" ]; then
echo "Importing 20_users.ldif..."
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=qsfera,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/20_users.ldif
fi
if [ -f "/opt/bitnami/openldap/ldifs/30_groups.ldif" ]; then
echo "Importing 30_groups.ldif..."
ldapadd -x -H ldap://localhost:1389 -D "cn=admin,dc=qsfera,dc=eu" -w admin -f /opt/bitnami/openldap/ldifs/30_groups.ldif
fi
echo "LDIF import completed!"
wait $ENTRYPOINT_PID
@@ -0,0 +1,70 @@
{
"services": {
"CoAuthoring": {
"sql": {
"type": "postgres",
"dbHost": "localhost",
"dbPort": "5432",
"dbName": "onlyoffice",
"dbUser": "onlyoffice",
"dbPass": "onlyoffice"
},
"token": {
"enable": {
"request": {
"inbox": true,
"outbox": true
},
"browser": true
},
"inbox": {
"header": "Authorization"
},
"outbox": {
"header": "Authorization"
}
},
"secret": {
"inbox": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
},
"outbox": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
},
"session": {
"string": "B8LjkNqGxn6gf8bkuBUiMwyuCFwFddnu"
}
}
}
},
"rabbitmq": {
"url": "amqp://guest:guest@localhost"
},
"FileConverter": {
"converter": {
"inputLimits": [
{
"type": "docx;dotx;docm;dotm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
},
{
"type": "xlsx;xltx;xlsm;xltm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
},
{
"type": "pptx;ppsx;potx;pptm;ppsm;potm",
"zip": {
"uncompressed": "1GB",
"template": "*.xml"
}
}
]
}
}
}
@@ -0,0 +1,46 @@
[
{
"name": "qsfera-server",
"full_name": "first-qsfera-instance",
"organization": "КуСфера",
"domain": "qsfera-server:9200",
"homepage": "https://qsfera.com",
"services": [
{
"endpoint": {
"type": {
"name": "OCM",
"description": "CERNBox Open Cloud Mesh API"
},
"name": "CERNBox - OCM API",
"path": "https://qsfera-server:9200/ocm/",
"is_monitored": true
},
"api_version": "0.0.1",
"host": "qsfera-server:9200"
}
]
},
{
"name": "federation-qsfera-server",
"full_name": "Federation qsfera",
"organization": "КуСфера",
"domain": "federation-qsfera-server:10200",
"homepage": "https://qsfera.com",
"services": [
{
"endpoint": {
"type": {
"name": "OCM",
"description": "CERNBox Open Cloud Mesh API"
},
"name": "CERNBox - OCM API",
"path": "https://federation-qsfera-server:10200/ocm/",
"is_monitored": true
},
"api_version": "0.0.1",
"host": "federation-qsfera-server:10200"
}
]
}
]
@@ -0,0 +1,30 @@
{
"server": "https://qsfera-server:9200",
"theme": "https://qsfera-server:9200/themes/qsfera/theme.json",
"version": "0.1.0",
"openIdConnect": {
"metadata_url": "https://qsfera-server:9200/.well-known/openid-configuration",
"authority": "https://qsfera-server:9200",
"client_id": "web",
"response_type": "code",
"scope": "openid profile email"
},
"options": {
"topCenterNotifications": true,
"displayResourcesLazy": false,
"sidebar": {
"shares": {
"showAllOnLoad": true
}
}
},
"apps": [
"files",
"text-editor",
"preview",
"pdf-viewer",
"search",
"admin-settings",
"app-store"
]
}
@@ -0,0 +1,22 @@
#!/bin/sh
set -e
if [ "$1" = "--qsfera-log" ]; then
sshpass -p "$SSH_OC_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_OC_USERNAME@$SSH_OC_REMOTE" "bash ~/scripts/qsfera.sh log"
exit 0
fi
# start КуСфера server
sshpass -p "$SSH_OC_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_OC_USERNAME@$SSH_OC_REMOTE" \
"OC_URL=${TEST_SERVER_URL} \
OC_COMMIT_ID=${DRONE_COMMIT} \
bash ~/scripts/qsfera.sh start"
# start k6 tests
sshpass -p "$SSH_K6_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_K6_USERNAME@$SSH_K6_REMOTE" \
"TEST_SERVER_URL=${TEST_SERVER_URL} \
bash ~/scripts/k6-tests.sh"
# stop КуСфера server
sshpass -p "$SSH_OC_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_OC_USERNAME@$SSH_OC_REMOTE" "bash ~/scripts/qsfera.sh stop"
@@ -0,0 +1,5 @@
#!/bin/sh
while true; do
echo -e "HTTP/1.1 200 OK\n\n$(cat tests/config/woodpecker/hosting-discovery.xml)" | nc -l -k -p 8080
done
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
SHARE_ENDPOINT="ocs/v2.php/apps/files_sharing/api/v1/shares"
# envs
ENV="SPACE_ID="
# get space id
SPACE_ID=$(curl -ks -uadmin:admin "${TEST_SERVER_URL}/graph/v1.0/me/drives" | jq -r '.value[] | select(.driveType == "personal") | .root.webDavUrl' | cut -d"/" -f6 | sed "s/\\$/\\\\$/g")
ENV+=${SPACE_ID}
# create a folder
curl -ks -ualan:demo -X MKCOL "${TEST_SERVER_URL}/remote.php/webdav/new_folder"
SHARE_ID=$(curl -ks -ualan:demo "${TEST_SERVER_URL}/${SHARE_ENDPOINT}" -d "path=/new_folder&shareType=0&permissions=15&name=new_folder&shareWith=admin" | grep -oP "(?<=<id>).*(?=</id>)")
# accept share
if [ ! -z "${SHARE_ID}" ];
then
curl -XPOST -ks -uadmin:admin "${TEST_SERVER_URL}/${SHARE_ENDPOINT}/pending/${SHARE_ID}"
fi
# create public share
PUBLIC_TOKEN=$(curl -ks -ualan:demo "${TEST_SERVER_URL}/${SHARE_ENDPOINT}" -d "path=/new_folder&shareType=3&permissions=15&name=new_folder" | grep -oP "(?<=<token>).*(?=</token>)")
ENV+="\nPUBLIC_TOKEN="
ENV+=${PUBLIC_TOKEN}
# create an .env file in the repo root dir
echo -e $ENV >> .env
@@ -0,0 +1,91 @@
const fs = require("node:fs");
const { minimatch } = require("minimatch");
const type = process.argv[2];
function getSkipPatterns(type) {
const base = [
".github/**",
".vscode/**",
"docs/**",
"deployments/**",
"CHANGELOG.md",
"CONTRIBUTING.md",
"LICENSE",
"README.md",
];
const unit = ["**/*_test.go"];
const acceptance = ["tests/acceptance/**"];
if (
type === "acceptance-tests" ||
type === "e2e-tests" ||
type === "lint"
) {
return [...base, ...unit];
}
if (type === "unit-tests") {
return [...base, ...acceptance];
}
if (
type === "build-binary" ||
type === "build-docker" ||
type === "litmus"
) {
return [...base, ...unit, ...acceptance];
}
if (type === "cache" || type === "base") {
return base;
}
return [];
}
function main() {
const skipPatterns = getSkipPatterns(type);
if (skipPatterns.length === 0) {
process.exit(0);
}
const rawFiles = process.env.CI_PIPELINE_FILES;
// If CI_PIPELINE_FILES is not set, we assume the pipeline should run
if (!rawFiles) {
console.log("[INFO] CI_PIPELINE_FILES not set → run pipeline");
process.exit(0);
}
let changedFiles;
try {
changedFiles = JSON.parse(rawFiles);
} catch {
console.error("[ERROR] Failed to parse CI_PIPELINE_FILES:", rawFiles);
process.exit(1);
}
if (changedFiles.length === 0) {
process.exit(0);
}
console.log("[INFO] Changed files:", changedFiles);
const onlySkippable = changedFiles.every((file) =>
skipPatterns.some((pattern) =>
minimatch(file, pattern, { dot: true, matchBase: true })
)
);
if (onlySkippable) {
console.log("[INFO] Only skippable files changed → SKIP WORKFLOW");
fs.appendFileSync(".woodpecker.env", "SKIP_WORKFLOW=true\n");
} else {
console.log("[INFO] Relevant changes detected → run pipeline");
}
}
main();
@@ -0,0 +1,29 @@
#!/bin/bash
set -e
CACHE_KEY="$PUBLIC_BUCKET/$CI_REPO_NAME/pipelines/$CI_COMMIT_SHA-$CI_PIPELINE_EVENT"
mc alias set s3 $MC_HOST $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY
# check previous pipeline
URL="https://s3.ci.qsfera.eu/$CACHE_KEY/prev_pipeline"
status=$(curl -s -o prev_pipeline "$URL" -w '%{http_code}')
if [ "$status" == "200" ];
then
source prev_pipeline
REPO_ID=$(printf '%s' "$CI_PIPELINE_URL" | sed 's|.*/repos/\([0-9]*\)/.*|\1|')
p_status=$(curl -s -o pipeline_info.json "$CI_SYSTEM_URL/api/repos/$REPO_ID/pipelines/$PREV_PIPELINE_NUMBER" -w "%{http_code}")
if [ "$p_status" != "200" ];
then
echo -e "[ERROR] Failed to fetch previous pipeline info.\n URL: $CI_SYSTEM_URL/api/repos/$REPO_ID/pipelines/$PREV_PIPELINE_NUMBER\n Status: $p_status"
exit 1
fi
# update previous pipeline info
mc cp -a pipeline_info.json "s3/$CACHE_KEY/"
fi
# upload current pipeline number for the next pipeline
echo "PREV_PIPELINE_NUMBER=$CI_PIPELINE_NUMBER" > prev_pipeline
mc cp -a prev_pipeline "s3/$CACHE_KEY/"
@@ -0,0 +1,131 @@
#
# This config is based on https://github.com/cs3org/wopiserver/blob/master/wopiserver.conf
#
# wopiserver.conf
#
# Default configuration file for the WOPI server for КуСфера
#
##############################################################
[general]
# Storage access layer to be loaded in order to operate this WOPI server
# only "cs3" is supported with КуСфера
storagetype = cs3
# Port where to listen for WOPI requests
port = 9300
# Logging level. Debug enables the Flask debug mode as well.
# Valid values are: Debug, Info, Warning, Error.
loglevel = Debug
loghandler = stream
logdest = stdout
# URL of your WOPI server or your HA proxy in front of it
wopiurl = http://wopiserver
# URL for direct download of files. The complete URL that is sent
# to clients will include the access_token argument
downloadurl = http://wopiserver/wopi/cbox/download
# The internal server engine to use (defaults to flask).
# Set to waitress for production installations.
internalserver = waitress
# List of file extensions deemed incompatible with LibreOffice:
# interoperable locking will be disabled for such files
nonofficetypes = .md .zmd .txt .epd
# List of file extensions to be supported by Collabora (deprecated)
codeofficetypes = .odt .ott .ods .ots .odp .otp .odg .otg .doc .dot .xls .xlt .xlm .ppt .pot .pps .vsd .dxf .wmf .cdr .pages .number .key
brandingname=CS3org WOPI server
brandingurl=https://github.com/cs3org/wopiserver
# WOPI access token expiration time [seconds]
tokenvalidity = 86400
# WOPI lock expiration time [seconds]
wopilockexpiration = 3600
# WOPI lock strict check: if True, WOPI locks will be compared according to specs,
# that is their representation must match. False (default) allows for a more relaxed
# comparison, which compensates incorrect lock requests from Microsoft Office Online
# on-premise setups.
wopilockstrictcheck = False
# Enable support of rename operations from WOPI apps. This is currently
# disabled by default as it has been observed that both MS Office and Collabora
# Online do not play well with this feature.
# Not supported with КуСфера, must always be set to "False"
enablerename = False
# Detection of external Microsoft Office or LibreOffice locks. By default, lock files
# compatible with Office for Desktop applications are detected, assuming that the
# underlying storage can be mounted as a remote filesystem: in this case, WOPI GetLock
# and SetLock operations return such locks and prevent online apps from entering edit mode.
# This feature can be disabled in order to operate a pure WOPI server for online apps.
# Not supported with КуСфера, must always be set to "False"
detectexternallocks = False
# Location of the webconflict files. By default, such files are stored in the same path
# as the original file. If that fails (e.g. because of missing permissions),
# an attempt is made to store such files in this path if specified, otherwise
# the system falls back to the recovery space (cf. io|recoverypath).
# The keywords <user_initial> and <username> are replaced with the actual username's
# initial letter and the actual username, respectively, so you can use e.g.
# /your_storage/home/user_initial/username
#conflictpath = /
# КуСфера's WOPI proxy configuration. Disabled by default.
#wopiproxy = https://external-wopi-proxy.com
#wopiproxysecretfile = /path/to/your/shared-key-file
#proxiedappname = Name of your proxied app
[security]
# Location of the secret files. Requires a restart of the
# WOPI server when either the files or their content change.
wopisecretfile = /etc/wopi/wopisecret
# iop secret is not used for cs3 storage type
#iopsecretfile = /etc/wopi/iopsecret
# Use https as opposed to http (requires certificate)
usehttps = no
# Certificate and key for https. Requires a restart
# to apply a change.
wopicert = /etc/grid-security/host.crt
wopikey = /etc/grid-security/host.key
[bridge]
# SSL certificate check for the connected apps
sslverify = True
# Minimal time interval between two consecutive save operations [seconds]
#saveinterval = 200
# Minimal time interval before a closed file is WOPI-unlocked [seconds]
#unlockinterval = 90
# CodiMD: disable creating zipped bundles when files contain pictures
#disablezip = False
[io]
# Size used for buffered reads [bytes]
chunksize = 4194304
# Path to a recovery space in case of I/O errors when reaching to the remote storage.
# This is expected to be a local path, and it is provided in order to ease user support.
# Defaults to the indicated spool folder.
recoverypath = /var/spool/wopirecovery
[cs3]
# Host and port of the Reva(-like) CS3-compliant GRPC gateway endpoint
revagateway = qsfera-server:9142
# Reva/gRPC authentication token expiration time [seconds]
# The default value matches Reva's default
authtokenvalidity = 3600
# SSL certificate check for Reva
sslverify = False