mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-28 21:17:36 +00:00
Merge pull request #903 from CyberMind-FR/fix/nextcloud-connection-urls
Some checks are pending
License Headers / check (push) Waiting to run
Some checks are pending
License Headers / check (push) Waiting to run
fix(nextcloud): connection URLs use real public domain not localhost:8080
This commit is contained in:
commit
aacbb39fb8
|
|
@ -16,6 +16,24 @@ from secubox_core.config import get_config
|
||||||
app = FastAPI(title="SecuBox Nextcloud")
|
app = FastAPI(title="SecuBox Nextcloud")
|
||||||
config = get_config("nextcloud")
|
config = get_config("nextcloud")
|
||||||
|
|
||||||
|
|
||||||
|
def _public_base_url(ssl_domain: str, domain: str, http_port: int) -> str:
|
||||||
|
"""Return the URL a client/device uses to reach Nextcloud.
|
||||||
|
|
||||||
|
HAProxy terminates TLS 1.3 in front of every SecuBox vhost, so a real
|
||||||
|
public domain is always reachable over https. Precedence: an explicit
|
||||||
|
ssl_domain wins; otherwise the configured public ``domain`` (skipping the
|
||||||
|
``cloud.local`` placeholder); only a bare host with no real domain falls
|
||||||
|
back to the container port. Never emit ``localhost`` when a domain exists —
|
||||||
|
that is unreachable from a phone/desktop client (the bug this fixes).
|
||||||
|
"""
|
||||||
|
if ssl_domain:
|
||||||
|
return f"https://{ssl_domain}"
|
||||||
|
if "." in domain and domain != "cloud.local":
|
||||||
|
return f"https://{domain}"
|
||||||
|
return f"http://localhost:{http_port}"
|
||||||
|
|
||||||
|
|
||||||
LXC_NAME = config.get("container_name", "nextcloud")
|
LXC_NAME = config.get("container_name", "nextcloud")
|
||||||
LXC_PATH = Path(config.get("lxc_path", "/data/lxc"))
|
LXC_PATH = Path(config.get("lxc_path", "/data/lxc"))
|
||||||
DATA_PATH = Path(config.get("data_path", "/data/volumes/nextcloud"))
|
DATA_PATH = Path(config.get("data_path", "/data/volumes/nextcloud"))
|
||||||
|
|
@ -158,8 +176,7 @@ async def status():
|
||||||
"disk_used": disk_used,
|
"disk_used": disk_used,
|
||||||
# Public URL from the real domain (not localhost) so the dashboard links
|
# Public URL from the real domain (not localhost) so the dashboard links
|
||||||
# somewhere reachable; falls back to the container port for a bare host.
|
# somewhere reachable; falls back to the container port for a bare host.
|
||||||
"web_url": f"https://{domain}" if "." in domain and domain != "cloud.local"
|
"web_url": _public_base_url(config.get("ssl_domain", ""), domain, http_port),
|
||||||
else f"http://localhost:{http_port}",
|
|
||||||
"ssl_enabled": config.get("ssl_enabled", False),
|
"ssl_enabled": config.get("ssl_enabled", False),
|
||||||
"container_name": LXC_NAME,
|
"container_name": LXC_NAME,
|
||||||
}
|
}
|
||||||
|
|
@ -443,12 +460,10 @@ def restore_backup(name: str):
|
||||||
async def get_connections():
|
async def get_connections():
|
||||||
"""Get connection URLs (CalDAV, CardDAV, WebDAV)"""
|
"""Get connection URLs (CalDAV, CardDAV, WebDAV)"""
|
||||||
http_port = config.get("http_port", 8080)
|
http_port = config.get("http_port", 8080)
|
||||||
ssl_enabled = config.get("ssl_enabled", False)
|
domain = config.get("domain", "cloud.local")
|
||||||
ssl_domain = config.get("ssl_domain", "")
|
ssl_domain = config.get("ssl_domain", "")
|
||||||
|
|
||||||
base_url = f"http://localhost:{http_port}"
|
base_url = _public_base_url(ssl_domain, domain, http_port)
|
||||||
if ssl_enabled and ssl_domain:
|
|
||||||
base_url = f"https://{ssl_domain}"
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"base_url": base_url,
|
"base_url": base_url,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,15 @@
|
||||||
|
secubox-nextcloud (1.5.1-1~bookworm1) bookworm; urgency=medium
|
||||||
|
|
||||||
|
* Fix connection URLs showing http://localhost:8080 instead of the real
|
||||||
|
public domain. /connections only checked ssl_domain (unset in
|
||||||
|
secubox.conf [nextcloud], which carries `domain`) and fell back to
|
||||||
|
localhost; the panel also hardcoded localhost, ignoring status.web_url.
|
||||||
|
New pure _public_base_url() helper (ssl_domain > domain > localhost),
|
||||||
|
used by both /connections and /status; panel now renders status.web_url.
|
||||||
|
Regression tests added (tests/test_public_base_url.py).
|
||||||
|
|
||||||
|
-- Gerald KERMA <devel@cybermind.fr> Fri, 25 Jul 2026 12:00:00 +0200
|
||||||
|
|
||||||
secubox-nextcloud (1.5.0-1~bookworm2) bookworm; urgency=medium
|
secubox-nextcloud (1.5.0-1~bookworm2) bookworm; urgency=medium
|
||||||
|
|
||||||
* Phase 2: Requires=secubox-core.service -> Wants= on this module's unit
|
* Phase 2: Requires=secubox-core.service -> Wants= on this module's unit
|
||||||
|
|
|
||||||
52
packages/secubox-nextcloud/tests/test_public_base_url.py
Normal file
52
packages/secubox-nextcloud/tests/test_public_base_url.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||||
|
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||||
|
# Source-Disclosed License — All rights reserved except as expressly granted.
|
||||||
|
# See LICENCE-CMSD-1.0.md for terms.
|
||||||
|
|
||||||
|
"""
|
||||||
|
SecuBox-Deb :: Nextcloud — _public_base_url unit tests
|
||||||
|
|
||||||
|
Regression guard: /connections must never hand a client localhost when a
|
||||||
|
real public domain is configured (clients reach Nextcloud through HAProxy
|
||||||
|
TLS 1.3). The helper is pure so it is tested without importing the FastAPI
|
||||||
|
app (which starts a cache thread + LXC probes at import time).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import textwrap
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MAIN = Path(__file__).resolve().parent.parent / "api" / "main.py"
|
||||||
|
|
||||||
|
|
||||||
|
def _load_helper():
|
||||||
|
"""Extract and exec only the _public_base_url function from api/main.py."""
|
||||||
|
tree = ast.parse(MAIN.read_text())
|
||||||
|
for node in tree.body:
|
||||||
|
if isinstance(node, ast.FunctionDef) and node.name == "_public_base_url":
|
||||||
|
src = textwrap.dedent(ast.get_source_segment(MAIN.read_text(), node))
|
||||||
|
ns: dict = {}
|
||||||
|
exec(src, ns)
|
||||||
|
return ns["_public_base_url"]
|
||||||
|
raise AssertionError("_public_base_url not found in api/main.py")
|
||||||
|
|
||||||
|
|
||||||
|
_public_base_url = _load_helper()
|
||||||
|
|
||||||
|
|
||||||
|
def test_configured_domain_yields_https_not_localhost():
|
||||||
|
# The bug: secubox.conf [nextcloud] has domain but no ssl_domain.
|
||||||
|
assert _public_base_url("", "nc.gk2.secubox.in", 8080) == "https://nc.gk2.secubox.in"
|
||||||
|
|
||||||
|
|
||||||
|
def test_ssl_domain_takes_precedence():
|
||||||
|
assert _public_base_url("secure.example.com", "nc.gk2.secubox.in", 8080) == \
|
||||||
|
"https://secure.example.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_placeholder_domain_falls_back_to_localhost():
|
||||||
|
assert _public_base_url("", "cloud.local", 8080) == "http://localhost:8080"
|
||||||
|
|
||||||
|
|
||||||
|
def test_bare_host_without_dot_falls_back_to_localhost():
|
||||||
|
assert _public_base_url("", "nextcloud", 9000) == "http://localhost:9000"
|
||||||
|
|
@ -237,9 +237,11 @@
|
||||||
document.getElementById('data-size').textContent = status.disk_used || '-';
|
document.getElementById('data-size').textContent = status.disk_used || '-';
|
||||||
document.getElementById('version').textContent = status.version || '-';
|
document.getElementById('version').textContent = status.version || '-';
|
||||||
|
|
||||||
const port = status.http_port || 8080;
|
// Use the public URL the API resolves (real domain behind HAProxy
|
||||||
document.getElementById('web-url').textContent = `http://localhost:${port}`;
|
// TLS), not a hardcoded localhost the client can't reach.
|
||||||
document.getElementById('web-url').href = `http://localhost:${port}`;
|
const webUrl = status.web_url || `http://localhost:${status.http_port || 8080}`;
|
||||||
|
document.getElementById('web-url').textContent = webUrl;
|
||||||
|
document.getElementById('web-url').href = webUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadStorage() {
|
async function loadStorage() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user