mirror of
https://github.com/nextcloud/all-in-one.git
synced 2026-02-15 18:20:20 +00:00
feat(app-api): add HaRP container
Signed-off-by: Oleksander Piskun <oleksandr2088@icloud.com> Signed-off-by: bigcat88 <bigcat88@icloud.com>
This commit is contained in:
parent
88a45d1a80
commit
a18ef97ab4
13 changed files with 126 additions and 7 deletions
|
|
@ -58,6 +58,11 @@ http://{$APACHE_HOST}:23973, # For Collabora callback and WOPI requests, see con
|
||||||
reverse_proxy {$WHITEBOARD_HOST}:3002
|
reverse_proxy {$WHITEBOARD_HOST}:3002
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# HaRP (ExApps)
|
||||||
|
route /exapps/* {
|
||||||
|
reverse_proxy {$HARP_HOST}:8780
|
||||||
|
}
|
||||||
|
|
||||||
# Nextcloud
|
# Nextcloud
|
||||||
route {
|
route {
|
||||||
header Strict-Transport-Security max-age=31536000;
|
header Strict-Transport-Security max-age=31536000;
|
||||||
|
|
|
||||||
|
|
@ -1020,13 +1020,13 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Docker socket proxy
|
# Docker socket proxy / HaRP
|
||||||
# app_api is a shipped app
|
# app_api is a shipped app
|
||||||
if [ -d "/var/www/html/custom_apps/app_api" ]; then
|
if [ -d "/var/www/html/custom_apps/app_api" ]; then
|
||||||
php /var/www/html/occ app:disable app_api
|
php /var/www/html/occ app:disable app_api
|
||||||
rm -r "/var/www/html/custom_apps/app_api"
|
rm -r "/var/www/html/custom_apps/app_api"
|
||||||
fi
|
fi
|
||||||
if [ "$DOCKER_SOCKET_PROXY_ENABLED" = 'yes' ]; then
|
if [ "$DOCKER_SOCKET_PROXY_ENABLED" = 'yes' ] || [ "$HARP_ENABLED" = 'yes' ]; then
|
||||||
if [ "$(php /var/www/html/occ config:app:get app_api enabled)" != "yes" ]; then
|
if [ "$(php /var/www/html/occ config:app:get app_api enabled)" != "yes" ]; then
|
||||||
php /var/www/html/occ app:enable app_api
|
php /var/www/html/occ app:enable app_api
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ You can run the containers that are build for AIO with docker-compose. This come
|
||||||
- You lose the AIO interface
|
- You lose the AIO interface
|
||||||
- You lose update notifications and automatic updates
|
- You lose update notifications and automatic updates
|
||||||
- You lose all AIO backup and restore features
|
- You lose all AIO backup and restore features
|
||||||
- You lose the built-in [Docker Socket Proxy container](https://github.com/nextcloud/docker-socket-proxy#readme) (needed for [Nextcloud App API](https://github.com/nextcloud/app_api#nextcloud-appapi))
|
- You lose the built-in [Docker Socket Proxy container](https://github.com/nextcloud/docker-socket-proxy#readme) and [HaRP container](https://github.com/nextcloud/HaRP) (needed for [Nextcloud App API](https://github.com/nextcloud/app_api#nextcloud-appapi))
|
||||||
- You lose all community containers: https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers
|
- You lose all community containers: https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers
|
||||||
- **You need to know what you are doing, especially when modifying the compose.yaml file**
|
- **You need to know what you are doing, especially when modifying the compose.yaml file**
|
||||||
- For updating, you need to strictly follow the at the bottom described update routine
|
- For updating, you need to strictly follow the at the bottom described update routine
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "next
|
||||||
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-borgbackup"))')"
|
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-borgbackup"))')"
|
||||||
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-docker-socket-proxy"))')"
|
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-docker-socket-proxy"))')"
|
||||||
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= if contains(["nextcloud-aio-docker-socket-proxy"]) then del(.[index("nextcloud-aio-docker-socket-proxy")]) else . end else . end')"
|
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= if contains(["nextcloud-aio-docker-socket-proxy"]) then del(.[index("nextcloud-aio-docker-socket-proxy")]) else . end else . end')"
|
||||||
|
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-harp"))')"
|
||||||
|
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= if contains(["nextcloud-aio-harp"]) then del(.[index("nextcloud-aio-harp")]) else . end else . end')"
|
||||||
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= map({ (.): { "condition": "service_started", "required": false } }) else . end' | jq '.services[] |= if has("depends_on") then .depends_on |= reduce .[] as $item ({}; . + $item) else . end')"
|
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= map({ (.): { "condition": "service_started", "required": false } }) else . end' | jq '.services[] |= if has("depends_on") then .depends_on |= reduce .[] as $item ({}; . + $item) else . end')"
|
||||||
|
|
||||||
sudo snap install yq
|
sudo snap install yq
|
||||||
|
|
@ -45,6 +47,8 @@ sed -i 's|- ip_binding: |- |' containers.yml
|
||||||
sed -i '/AIO_TOKEN/d' containers.yml
|
sed -i '/AIO_TOKEN/d' containers.yml
|
||||||
sed -i '/AIO_URL/d' containers.yml
|
sed -i '/AIO_URL/d' containers.yml
|
||||||
sed -i '/DOCKER_SOCKET_PROXY_ENABLED/d' containers.yml
|
sed -i '/DOCKER_SOCKET_PROXY_ENABLED/d' containers.yml
|
||||||
|
sed -i '/HARP_ENABLED/d' containers.yml
|
||||||
|
sed -i '/HP_SHARED_KEY/d' containers.yml
|
||||||
sed -i '/ADDITIONAL_TRUSTED_PROXY/d' containers.yml
|
sed -i '/ADDITIONAL_TRUSTED_PROXY/d' containers.yml
|
||||||
sed -i '/TURN_DOMAIN/d' containers.yml
|
sed -i '/TURN_DOMAIN/d' containers.yml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
"nextcloud-aio-talk",
|
"nextcloud-aio-talk",
|
||||||
"nextcloud-aio-notify-push",
|
"nextcloud-aio-notify-push",
|
||||||
"nextcloud-aio-whiteboard",
|
"nextcloud-aio-whiteboard",
|
||||||
|
"nextcloud-aio-harp",
|
||||||
"nextcloud-aio-nextcloud"
|
"nextcloud-aio-nextcloud"
|
||||||
],
|
],
|
||||||
"display_name": "Apache",
|
"display_name": "Apache",
|
||||||
|
|
@ -49,7 +50,8 @@
|
||||||
"APACHE_MAX_SIZE=%APACHE_MAX_SIZE%",
|
"APACHE_MAX_SIZE=%APACHE_MAX_SIZE%",
|
||||||
"APACHE_MAX_TIME=%NEXTCLOUD_MAX_TIME%",
|
"APACHE_MAX_TIME=%NEXTCLOUD_MAX_TIME%",
|
||||||
"NOTIFY_PUSH_HOST=nextcloud-aio-notify-push",
|
"NOTIFY_PUSH_HOST=nextcloud-aio-notify-push",
|
||||||
"WHITEBOARD_HOST=nextcloud-aio-whiteboard"
|
"WHITEBOARD_HOST=nextcloud-aio-whiteboard",
|
||||||
|
"HARP_HOST=nextcloud-aio-harp"
|
||||||
],
|
],
|
||||||
"volumes": [
|
"volumes": [
|
||||||
{
|
{
|
||||||
|
|
@ -146,7 +148,8 @@
|
||||||
"nextcloud-aio-fulltextsearch",
|
"nextcloud-aio-fulltextsearch",
|
||||||
"nextcloud-aio-talk-recording",
|
"nextcloud-aio-talk-recording",
|
||||||
"nextcloud-aio-imaginary",
|
"nextcloud-aio-imaginary",
|
||||||
"nextcloud-aio-docker-socket-proxy"
|
"nextcloud-aio-docker-socket-proxy",
|
||||||
|
"nextcloud-aio-harp"
|
||||||
],
|
],
|
||||||
"display_name": "Nextcloud",
|
"display_name": "Nextcloud",
|
||||||
"image": "ghcr.io/nextcloud-releases/aio-nextcloud",
|
"image": "ghcr.io/nextcloud-releases/aio-nextcloud",
|
||||||
|
|
@ -172,7 +175,8 @@
|
||||||
"SIGNALING_SECRET",
|
"SIGNALING_SECRET",
|
||||||
"FULLTEXTSEARCH_PASSWORD",
|
"FULLTEXTSEARCH_PASSWORD",
|
||||||
"IMAGINARY_SECRET",
|
"IMAGINARY_SECRET",
|
||||||
"WHITEBOARD_SECRET"
|
"WHITEBOARD_SECRET",
|
||||||
|
"HP_SHARED_KEY"
|
||||||
],
|
],
|
||||||
"volumes": [
|
"volumes": [
|
||||||
{
|
{
|
||||||
|
|
@ -257,7 +261,9 @@
|
||||||
"THIS_IS_AIO=true",
|
"THIS_IS_AIO=true",
|
||||||
"IMAGINARY_SECRET=%IMAGINARY_SECRET%",
|
"IMAGINARY_SECRET=%IMAGINARY_SECRET%",
|
||||||
"WHITEBOARD_SECRET=%WHITEBOARD_SECRET%",
|
"WHITEBOARD_SECRET=%WHITEBOARD_SECRET%",
|
||||||
"WHITEBOARD_ENABLED=%WHITEBOARD_ENABLED%"
|
"WHITEBOARD_ENABLED=%WHITEBOARD_ENABLED%",
|
||||||
|
"HARP_ENABLED=%HARP_ENABLED%",
|
||||||
|
"HP_SHARED_KEY=%HP_SHARED_KEY%"
|
||||||
],
|
],
|
||||||
"stop_grace_period": 600,
|
"stop_grace_period": 600,
|
||||||
"restart": "unless-stopped",
|
"restart": "unless-stopped",
|
||||||
|
|
@ -846,6 +852,51 @@
|
||||||
"NET_RAW"
|
"NET_RAW"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"container_name": "nextcloud-aio-harp",
|
||||||
|
"image_tag": "release",
|
||||||
|
"display_name": "HaRP",
|
||||||
|
"image": "ghcr.io/nextcloud/nextcloud-appapi-harp",
|
||||||
|
"init": true,
|
||||||
|
"internal_port": "8780",
|
||||||
|
"expose": [
|
||||||
|
"8780"
|
||||||
|
],
|
||||||
|
"environment": [
|
||||||
|
"HP_SHARED_KEY=%HP_SHARED_KEY%",
|
||||||
|
"NC_INSTANCE_URL=https://%NC_DOMAIN%",
|
||||||
|
"HP_LOG_LEVEL=warning",
|
||||||
|
"HP_FRP_DISABLE_TLS=true",
|
||||||
|
"TZ=%TIMEZONE%"
|
||||||
|
],
|
||||||
|
"secrets": [
|
||||||
|
"HP_SHARED_KEY"
|
||||||
|
],
|
||||||
|
"volumes": [
|
||||||
|
{
|
||||||
|
"source": "%WATCHTOWER_DOCKER_SOCKET_PATH%",
|
||||||
|
"destination": "/var/run/docker.sock",
|
||||||
|
"writeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "nextcloud_aio_harp",
|
||||||
|
"destination": "/certs",
|
||||||
|
"writeable": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"restart": "unless-stopped",
|
||||||
|
"read_only": true,
|
||||||
|
"tmpfs": [
|
||||||
|
"/tmp",
|
||||||
|
"/run/harp"
|
||||||
|
],
|
||||||
|
"cap_drop": [
|
||||||
|
"NET_RAW"
|
||||||
|
],
|
||||||
|
"backup_volumes": [
|
||||||
|
"nextcloud_aio_harp"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"container_name": "nextcloud-aio-whiteboard",
|
"container_name": "nextcloud-aio-whiteboard",
|
||||||
"image_tag": "%AIO_CHANNEL%",
|
"image_tag": "%AIO_CHANNEL%",
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,16 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleHarpWarning() {
|
||||||
|
if (document.getElementById("harp").checked) {
|
||||||
|
alert('⚠️ Warning! Enabling this container comes with possible Security problems since you are exposing the docker socket and all its privileges to the HaRP container. Enable this only if you are sure what you are doing!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize event listeners for specific behaviors
|
// Initialize event listeners for specific behaviors
|
||||||
document.getElementById("talk").addEventListener('change', handleTalkVisibility);
|
document.getElementById("talk").addEventListener('change', handleTalkVisibility);
|
||||||
document.getElementById("docker-socket-proxy").addEventListener('change', handleDockerSocketProxyWarning);
|
document.getElementById("docker-socket-proxy").addEventListener('change', handleDockerSocketProxyWarning);
|
||||||
|
document.getElementById("harp").addEventListener('change', handleHarpWarning);
|
||||||
|
|
||||||
// Initialize talk-recording visibility on page load
|
// Initialize talk-recording visibility on page load
|
||||||
handleTalkVisibility(); // Ensure talk-recording is correctly initialized
|
handleTalkVisibility(); // Ensure talk-recording is correctly initialized
|
||||||
|
|
|
||||||
7
php/public/disable-harp.js
Normal file
7
php/public/disable-harp.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
|
// HaRP
|
||||||
|
let harp = document.getElementById("harp");
|
||||||
|
if (harp) {
|
||||||
|
harp.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -136,6 +136,7 @@ $app->get('/containers', function (Request $request, Response $response, array $
|
||||||
'is_nvidia_gpu_enabled' => $configurationManager->isNvidiaGpuEnabled(),
|
'is_nvidia_gpu_enabled' => $configurationManager->isNvidiaGpuEnabled(),
|
||||||
'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled(),
|
'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled(),
|
||||||
'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled(),
|
'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled(),
|
||||||
|
'is_harp_enabled' => $configurationManager->isHarpEnabled(),
|
||||||
'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled(),
|
'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled(),
|
||||||
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
|
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
|
||||||
'community_containers_enabled' => $configurationManager->GetEnabledCommunityContainers(),
|
'community_containers_enabled' => $configurationManager->GetEnabledCommunityContainers(),
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ readonly class ContainerDefinitionFetcher {
|
||||||
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
|
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} elseif ($entry['container_name'] === 'nextcloud-aio-harp') {
|
||||||
|
if (!$this->configurationManager->isHarpEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') {
|
} elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') {
|
||||||
if (!$this->configurationManager->isWhiteboardEnabled()) {
|
if (!$this->configurationManager->isWhiteboardEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -199,6 +203,10 @@ readonly class ContainerDefinitionFetcher {
|
||||||
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
|
if (!$this->configurationManager->isDockerSocketProxyEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} elseif ($value === 'nextcloud-aio-harp') {
|
||||||
|
if (!$this->configurationManager->isHarpEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} elseif ($value === 'nextcloud-aio-whiteboard') {
|
} elseif ($value === 'nextcloud-aio-whiteboard') {
|
||||||
if (!$this->configurationManager->isWhiteboardEnabled()) {
|
if (!$this->configurationManager->isWhiteboardEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,11 @@ readonly class ConfigurationController {
|
||||||
} else {
|
} else {
|
||||||
$this->configurationManager->SetDockerSocketProxyEnabledState(0);
|
$this->configurationManager->SetDockerSocketProxyEnabledState(0);
|
||||||
}
|
}
|
||||||
|
if (isset($request->getParsedBody()['harp'])) {
|
||||||
|
$this->configurationManager->SetHarpEnabledState(1);
|
||||||
|
} else {
|
||||||
|
$this->configurationManager->SetHarpEnabledState(0);
|
||||||
|
}
|
||||||
if (isset($request->getParsedBody()['whiteboard'])) {
|
if (isset($request->getParsedBody()['whiteboard'])) {
|
||||||
$this->configurationManager->SetWhiteboardEnabledState(1);
|
$this->configurationManager->SetWhiteboardEnabledState(1);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,21 @@ class ConfigurationManager
|
||||||
$this->WriteConfig($config);
|
$this->WriteConfig($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isHarpEnabled() : bool {
|
||||||
|
$config = $this->GetConfig();
|
||||||
|
if (isset($config['isHarpEnabled']) && $config['isHarpEnabled'] === 1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetHarpEnabledState(int $value) : void {
|
||||||
|
$config = $this->GetConfig();
|
||||||
|
$config['isHarpEnabled'] = $value;
|
||||||
|
$this->WriteConfig($config);
|
||||||
|
}
|
||||||
|
|
||||||
public function isWhiteboardEnabled() : bool {
|
public function isWhiteboardEnabled() : bool {
|
||||||
$config = $this->GetConfig();
|
$config = $this->GetConfig();
|
||||||
if (isset($config['isWhiteboardEnabled']) && $config['isWhiteboardEnabled'] === 0) {
|
if (isset($config['isWhiteboardEnabled']) && $config['isWhiteboardEnabled'] === 0) {
|
||||||
|
|
|
||||||
|
|
@ -567,6 +567,7 @@ readonly class DockerActionManager {
|
||||||
'IMAGINARY_ENABLED' => $this->configurationManager->isImaginaryEnabled() ? 'yes' : '',
|
'IMAGINARY_ENABLED' => $this->configurationManager->isImaginaryEnabled() ? 'yes' : '',
|
||||||
'FULLTEXTSEARCH_ENABLED' => $this->configurationManager->isFulltextsearchEnabled() ? 'yes' : '',
|
'FULLTEXTSEARCH_ENABLED' => $this->configurationManager->isFulltextsearchEnabled() ? 'yes' : '',
|
||||||
'DOCKER_SOCKET_PROXY_ENABLED' => $this->configurationManager->isDockerSocketProxyEnabled() ? 'yes' : '',
|
'DOCKER_SOCKET_PROXY_ENABLED' => $this->configurationManager->isDockerSocketProxyEnabled() ? 'yes' : '',
|
||||||
|
'HARP_ENABLED' => $this->configurationManager->isHarpEnabled() ? 'yes' : '',
|
||||||
'NEXTCLOUD_UPLOAD_LIMIT' => $this->configurationManager->GetNextcloudUploadLimit(),
|
'NEXTCLOUD_UPLOAD_LIMIT' => $this->configurationManager->GetNextcloudUploadLimit(),
|
||||||
'NEXTCLOUD_MEMORY_LIMIT' => $this->configurationManager->GetNextcloudMemoryLimit(),
|
'NEXTCLOUD_MEMORY_LIMIT' => $this->configurationManager->GetNextcloudMemoryLimit(),
|
||||||
'NEXTCLOUD_MAX_TIME' => $this->configurationManager->GetNextcloudMaxTime(),
|
'NEXTCLOUD_MAX_TIME' => $this->configurationManager->GetNextcloudMaxTime(),
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,20 @@
|
||||||
>
|
>
|
||||||
<label for="docker-socket-proxy">Docker Socket Proxy (needed for <a target="_blank" href="https://github.com/cloud-py-api/app_api#nextcloud-appapi">Nextcloud App API</a>)</label>
|
<label for="docker-socket-proxy">Docker Socket Proxy (needed for <a target="_blank" href="https://github.com/cloud-py-api/app_api#nextcloud-appapi">Nextcloud App API</a>)</label>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="harp"
|
||||||
|
name="harp"
|
||||||
|
{% if is_harp_enabled == true %}
|
||||||
|
checked="checked"
|
||||||
|
data-initial-state="true"
|
||||||
|
{% else %}
|
||||||
|
data-initial-state="false"
|
||||||
|
{% endif %}
|
||||||
|
>
|
||||||
|
<label for="harp">HaRP (High-availability Reverse Proxy for <a target="_blank" href="https://github.com/nextcloud/HaRP">Nextcloud ExApps</a>)</label>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
@ -146,6 +160,7 @@
|
||||||
{% if isAnyRunning == true %}
|
{% if isAnyRunning == true %}
|
||||||
<script type="text/javascript" src="disable-clamav.js"></script>
|
<script type="text/javascript" src="disable-clamav.js"></script>
|
||||||
<script type="text/javascript" src="disable-docker-socket-proxy.js"></script>
|
<script type="text/javascript" src="disable-docker-socket-proxy.js"></script>
|
||||||
|
<script type="text/javascript" src="disable-harp.js"></script>
|
||||||
<script type="text/javascript" src="disable-talk.js"></script>
|
<script type="text/javascript" src="disable-talk.js"></script>
|
||||||
<script type="text/javascript" src="disable-collabora.js"></script>
|
<script type="text/javascript" src="disable-collabora.js"></script>
|
||||||
<script type="text/javascript" src="disable-onlyoffice.js"></script>
|
<script type="text/javascript" src="disable-onlyoffice.js"></script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue