Fix API docs link when app is served from the backend

The API docs nav entry uses the origin-relative path "/api/docs", which
Uri.parse left scheme- and host-less, so canLaunchUrl/launchUrl could not
open it (e.g. in Docker). Resolve external links against the current page
so they carry a scheme and host; absolute URLs pass through unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-06-06 16:50:43 -04:00
co-authored by Claude Opus 4.8
parent 37075294e3
commit b0222445fb
2 changed files with 36 additions and 1 deletions
+8 -1
View File
@@ -374,8 +374,15 @@ void _onDestinationSelected(_NavDest destination, BuildContext context) {
// Opens an external link in a new tab. Origin-relative paths (e.g. the API docs)
// resolve against the current host, since the backend serves both the front-end
// and the API docs from the same origin.
// Resolves an external link against [base] (defaulting to the current page).
// Origin-relative paths (e.g. "/api/docs") gain the current scheme and host so
// the resulting URI is launchable; absolute URLs are returned unchanged.
// Without this, launching a scheme-less URI fails when the app is served from
// the backend (e.g. Docker).
Uri resolveExternalUri(String url, {Uri? base}) => (base ?? Uri.base).resolve(url);
Future<void> _openExternal(String url) async {
final uri = Uri.parse(url);
final uri = resolveExternalUri(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
@@ -0,0 +1,28 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:frontend/navigation.dart';
void main() {
final origin = Uri.parse('http://192.168.1.10:8080/web/devices');
test('resolves an origin-relative path against the current host', () {
expect(
resolveExternalUri('/api/docs', base: origin),
Uri.parse('http://192.168.1.10:8080/api/docs'),
);
});
test('preserves the scheme and host of an https origin', () {
final secure = Uri.parse('https://example.com/web/');
expect(
resolveExternalUri('/api/docs', base: secure),
Uri.parse('https://example.com/api/docs'),
);
});
test('returns an absolute URL unchanged', () {
expect(
resolveExternalUri('https://docs.example.com/spec', base: origin),
Uri.parse('https://docs.example.com/spec'),
);
});
}