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
+127
View File
@@ -0,0 +1,127 @@
#!/bin/bash
export WEB_UI_CONFIG_FILE=tests/config/local/fed-qsfera-web.json
export OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE=tests/config/local/providers.json
export OCM_OCM_INVITE_MANAGER_INSECURE=true
export OCM_OCM_SHARE_PROVIDER_INSECURE=true
export OCM_OCM_STORAGE_PROVIDER_INSECURE=true
export OC_BASE_DATA_PATH=${HOME}/.qsfera-10200
export OC_CONFIG_DIR=${HOME}/.qsfera-10200/config
export OC_EVENTS_ENDPOINT=127.0.0.1:10233
export OC_LDAP_URI=ldaps://localhost:10235
export OC_RUNTIME_PORT=10250
export OC_URL=https://localhost:10200
export OC_ADD_RUN_SERVICES=ocm
export OC_ENABLE_OCM=true
export APP_PROVIDER_DEBUG_ADDR=127.0.0.1:10165
export APP_PROVIDER_GRPC_ADDR=127.0.0.1:10164
export APP_REGISTRY_DEBUG_ADDR=127.0.0.1:10243
export APP_REGISTRY_GRPC_ADDR=127.0.0.1:10242
export AUTH_APP_DEBUG_ADDR=127.0.0.1:10245
export AUTH_APP_GRPC_ADDR=127.0.0.1:10246
export AUTH_APP_HTTP_ADDR=127.0.0.1:10247
export AUTH_BASIC_DEBUG_ADDR=127.0.0.1:10147
export AUTH_BASIC_GRPC_ADDR=127.0.0.1:10146
export AUTH_MACHINE_DEBUG_ADDR=127.0.0.1:10167
export AUTH_MACHINE_GRPC_ADDR=127.0.0.1:10166
export AUTH_SERVICE_DEBUG_ADDR=127.0.0.1:10169
export AUTH_SERVICE_GRPC_ADDR=127.0.0.1:10199
export CLIENTLOG_DEBUG_ADDR=127.0.0.1:10260
export ACTIVITYLOG_HTTP_ADDR=127.0.0.1:10195
export ACTIVITYLOG_DEBUG_ADDR=127.0.0.1:10261
export EVENTHISTORY_GRPC_ADDR=127.0.0.1:10274
export EVENTHISTORY_DEBUG_ADDR=127.0.0.1:10270
export FRONTEND_DEBUG_ADDR=127.0.0.1:10141
export FRONTEND_HTTP_ADDR=127.0.0.1:10140
export GATEWAY_DEBUG_ADDR=127.0.0.1:10143
export GATEWAY_GRPC_ADDR=127.0.0.1:10142
export GRAPH_DEBUG_ADDR=127.0.0.1:10124
export GRAPH_HTTP_ADDR=127.0.0.1:10120
export GROUPS_DEBUG_ADDR=127.0.0.1:10161
export GROUPS_GRPC_ADDR=127.0.0.1:10160
export IDM_DEBUG_ADDR=127.0.0.1:10239
export IDM_LDAPS_ADDR=127.0.0.1:10235
export IDP_DEBUG_ADDR=127.0.0.1:10134
export IDP_HTTP_ADDR=127.0.0.1:10130
export NATS_DEBUG_ADDR=127.0.0.1:10234
export NATS_NATS_PORT=10233
export OCM_DEBUG_ADDR=127.0.0.1:10281
export OCM_HTTP_ADDR=127.0.0.1:10280
export OCM_GRPC_ADDR=127.0.0.1:10282
export OCM_OCM_STORAGE_DATA_SERVER_URL=http://localhost:10280/data
export OCS_DEBUG_ADDR=127.0.0.1:10114
export OCS_HTTP_ADDR=127.0.0.1:10110
export POSTPROCESSING_DEBUG_ADDR=127.0.0.1:10255
export POSTPROCESSING_SERVICE_DEBUG_ADDR=127.0.0.1:10255
export PROXY_DEBUG_ADDR=127.0.0.1:10205
export PROXY_HTTP_ADDR=127.0.0.1:10200
export SEARCH_DEBUG_ADDR=127.0.0.1:10224
export SEARCH_GRPC_ADDR=127.0.0.1:10220
export SETTINGS_DEBUG_ADDR=127.0.0.1:10189
export SETTINGS_GRPC_ADDR=127.0.0.1:10185
export SETTINGS_HTTP_ADDR=127.0.0.1:10186
export SHARING_DEBUG_ADDR=127.0.0.1:10151
export SHARING_GRPC_ADDR=127.0.0.1:10150
export SSE_DEBUG_ADDR=127.0.0.1:10135
export SSE_HTTP_ADDR=127.0.0.1:10132
export STORAGE_PUBLICLINK_DEBUG_ADDR=127.0.0.1:10179
export STORAGE_PUBLICLINK_GRPC_ADDR=127.0.0.1:10178
export STORAGE_SHARES_DEBUG_ADDR=127.0.0.1:10156
export STORAGE_SHARES_GRPC_ADDR=127.0.0.1:10154
export STORAGE_SYSTEM_DATA_SERVER_URL=http://localhost:10216/data
export STORAGE_SYSTEM_DEBUG_ADDR=127.0.0.1:10217
export STORAGE_SYSTEM_GRPC_ADDR=127.0.0.1:10215
export STORAGE_SYSTEM_HTTP_ADDR=127.0.0.1:10216
export STORAGE_USERS_DEBUG_ADDR=127.0.0.1:10159
export STORAGE_USERS_GRPC_ADDR=127.0.0.1:10157
export STORAGE_USERS_HTTP_ADDR=127.0.0.1:10158
export STORAGE_USERS_DATA_SERVER_URL=http://localhost:10158/data
export STORE_DEBUG_ADDR=127.0.0.1:10464
export STORE_GRPC_ADDR=127.0.0.1:10460
export THUMBNAILS_DEBUG_ADDR=127.0.0.1:10194
export THUMBNAILS_GRPC_ADDR=127.0.0.1:10191
export THUMBNAILS_HTTP_ADDR=127.0.0.1:10190
export THUMBNAILS_DATA_ENDPOINT=http://127.0.0.1:10190/thumbnails/data
export USERLOG_HTTP_ADDR=127.0.0.1:10211
export USERLOG_DEBUG_ADDR=127.0.0.1:10210
export USERS_DEBUG_ADDR=127.0.0.1:10145
export USERS_GRPC_ADDR=127.0.0.1:10144
export WEB_DEBUG_ADDR=localhost:10104
export WEB_HTTP_ADDR=localhost:10100
export WEBDAV_DEBUG_ADDR=127.0.0.1:10119
export WEBDAV_HTTP_ADDR=127.0.0.1:10115
export WEBFINGER_DEBUG_ADDR=127.0.0.1:10279
export WEBFINGER_HTTP_ADDR=127.0.0.1:10275
export OC_SERVICE_ACCOUNT_ID=serviceaccount
export OC_SERVICE_ACCOUNT_SECRET=serviceaccountsecret
export OC_DECOMPOSEDFS_PROPAGATOR=async
export STORAGE_USERS_ASYNC_PROPAGATOR_PROPAGATION_DELAY=10s
export TLS_INSECURE=true
export OC_INSECURE=true
export IDP_ACCESS_TOKEN_EXPIRATION=9000
export GATEWAY_STORAGE_USERS_MOUNT_ID=storage-users-1
export STORAGE_USERS_MOUNT_ID=storage-users-1
export MICRO_REGISTRY=memory
export SHARING_PUBLIC_DRIVER=jsoncs3
export FRONTEND_OCS_RESOURCE_INFO_CACHE_TTL=0
export OC_LOG_LEVEL=info
export OC_LOG_PRETTY=true
export OC_LOG_COLOR=true
export PROXY_ENABLE_BASIC_AUTH=true
export IDM_CREATE_DEMO_USERS=true
export OC_ADMIN_USER_ID=some-admin-user-id-0000-000000000000
export IDM_ADMIN_PASSWORD=admin
export OC_SYSTEM_USER_ID=some-system-user-id-000-000000000000
export OC_SYSTEM_USER_API_KEY=some-system-user-machine-auth-api-key
export OC_JWT_SECRET=some-qsfera-jwt-secret
export OC_MACHINE_AUTH_API_KEY=some-qsfera-machine-auth-api-key
export OC_TRANSFER_SECRET=some-qsfera-transfer-secret
export IDM_SVC_PASSWORD=some-ldap-idm-password
export GRAPH_LDAP_BIND_PASSWORD=some-ldap-idm-password
export IDM_REVASVC_PASSWORD=some-ldap-reva-password
export GROUPS_LDAP_BIND_PASSWORD=some-ldap-reva-password
export USERS_LDAP_BIND_PASSWORD=some-ldap-reva-password
export AUTH_BASIC_LDAP_BIND_PASSWORD=some-ldap-reva-password
export IDM_IDPSVC_PASSWORD=some-ldap-idp-password
export IDP_LDAP_BIND_PASSWORD=some-ldap-idp-password
export GRAPH_APPLICATION_ID=application-1export
@@ -0,0 +1,31 @@
{
"server": "https://localhost:10200",
"theme": "https://localhost:10200/themes/qsfera/theme.json",
"version": "0.1.0",
"openIdConnect": {
"metadata_url": "https://localhost:10200/.well-known/openid-configuration",
"authority": "https://localhost:10200",
"client_id": "web",
"response_type": "code",
"scope": "openid profile email"
},
"options": {
"topCenterNotifications": true,
"displayResourcesLazy": false,
"sidebar": {
"shares": {
"showAllOnLoad": true
}
}
},
"apps": [
"files",
"ocm",
"text-editor",
"preview",
"pdf-viewer",
"search",
"admin-settings",
"app-store"
]
}
+46
View File
@@ -0,0 +1,46 @@
[
{
"name": "qsfera-server",
"full_name": "first-qsfera-instance",
"organization": "КуСфера",
"domain": "localhost:9200",
"homepage": "https://qsfera.eu",
"services": [
{
"endpoint": {
"type": {
"name": "OCM",
"description": "CERNBox Open Cloud Mesh API"
},
"name": "CERNBox - OCM API",
"path": "https://localhost:9200/ocm/",
"is_monitored": true
},
"api_version": "0.0.1",
"host": "localhost:9200"
}
]
},
{
"name": "federation-qsfera-server",
"full_name": "Federation qsfera",
"organization": "КуСфера",
"domain": "localhost:10200",
"homepage": "https://qsfera.eu",
"services": [
{
"endpoint": {
"type": {
"name": "OCM",
"description": "CERNBox Open Cloud Mesh API"
},
"name": "CERNBox - OCM API",
"path": "https://localhost:10200/ocm/",
"is_monitored": true
},
"api_version": "0.0.1",
"host": "localhost:10200"
}
]
}
]
+31
View File
@@ -0,0 +1,31 @@
{
"server": "https://localhost:9200",
"theme": "https://localhost:9200/themes/qsfera/theme.json",
"version": "0.1.0",
"openIdConnect": {
"metadata_url": "https://localhost:9200/.well-known/openid-configuration",
"authority": "https://localhost:9200",
"client_id": "web",
"response_type": "code",
"scope": "openid profile email"
},
"options": {
"topCenterNotifications": true,
"displayResourcesLazy": false,
"sidebar": {
"shares": {
"showAllOnLoad": true
}
}
},
"apps": [
"files",
"ocm",
"text-editor",
"preview",
"pdf-viewer",
"search",
"admin-settings",
"app-store"
]
}
@@ -0,0 +1,3 @@
# Activity Log
msgid "{user} added {resource} to {folder}"
msgstr "{user} hat {resource} zu {folder} hinzugefügt"
@@ -0,0 +1,15 @@
# Email Notification
msgid "Hello {ShareGrantee}"
msgstr "Hallo {ShareGrantee}"
msgid "Hello {SpaceGrantee},"
msgstr "Hallo {SpaceGrantee},"
msgid "{ShareSharer} has shared \"{ShareFolder}\" with you."
msgstr "{ShareSharer} hat \"{ShareFolder}\" mit Ihnen geteilt."
msgid "Click here to view it: {ShareLink}"
msgstr "Zum Ansehen hier klicken: {ShareLink}"
msgid "{SpaceSharer} has invited you to join \"{SpaceName}\"."
msgstr "{SpaceSharer} hat Sie eingeladen, dem Space \"{SpaceName}\" beizutreten."
@@ -0,0 +1,25 @@
# Antivirus
msgid "Virus found"
msgstr "Virus gefunden"
msgid "Virus found in {resource}. Upload not possible. Virus: {virus}"
msgstr "In {resource} wurde potenziell schädlicher Code gefunden. Das Hochladen wurde abgebrochen. Grund: {virus}"
# User Log
msgid "Space shared"
msgstr "Space freigegeben"
msgid "{user} added you to Space {space}"
msgstr "{user} hat Sie zu Space {space} hinzugefügt"
msgid "Instance will be shut down and deprovisioned"
msgstr "Instanz wird heruntergefahren und außer Betrieb genommen werden."
msgid "Attention! The instance will be shut down and deprovisioned on {date}. Download all your data before that date as no access past that date is possible."
msgstr "Achtung! Diese Instanz wird am {date} heruntergefahren und außer Betrieb genommen werden. Laden Sie Ihre Daten vor diesem Tag herunter, da Sie danach nicht mehr darauf zugreifen können."
msgid "Resource shared"
msgstr "Neue Freigabe"
msgid "{user} shared {resource} with you"
msgstr "{user} hat {resource} mit Ihnen geteilt"
@@ -0,0 +1,15 @@
# Email Notification
msgid "Hello {ShareGrantee}"
msgstr "Hola {ShareGrantee}"
msgid "Hello {SpaceGrantee},"
msgstr "Hola {SpaceGrantee},"
msgid "{ShareSharer} has shared \"{ShareFolder}\" with you."
msgstr "{ShareSharer} ha compartido \"{ShareFolder}\" contigo."
msgid "Click here to view it: {ShareLink}"
msgstr "Click aquí para verlo: {ShareLink}"
msgid "{SpaceSharer} has invited you to join \"{SpaceName}\"."
msgstr "{SpaceSharer} te ha invitado a unirte a \"{SpaceName}\"."
@@ -0,0 +1,25 @@
# Antivirus
msgid "Virus found"
msgstr "Virus encontrado"
msgid "Virus found in {resource}. Upload not possible. Virus: {virus}"
msgstr "Virus encontrado en {resource}. La subida no ha sido posible. Virus: {virus}"
# User Log
msgid "Space shared"
msgstr "Space compartido"
msgid "{user} added you to Space {space}"
msgstr "{user} te añadió al Space {space}"
msgid "Instance will be shut down and deprovisioned"
msgstr "La instancia se cerrará y se desaprovisionará"
msgid "Attention! The instance will be shut down and deprovisioned on {date}. Download all your data before that date as no access past that date is possible."
msgstr "¡Atención! La instancia se cerrará y se desaprovisionará el {date}. Descarga todos tus datos antes de esa fecha, puesto que el acceso pasada la fecha no será posible."
msgid "Resource shared"
msgstr "Recurso compartido"
msgid "{user} shared {resource} with you"
msgstr "{user} compartió {resource} contigo"
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