import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; part 'dev_tools_settings.g.dart'; enum MarianumConnectEndpoint { live, beta, custom } @JsonSerializable() class DevToolsSettings { bool showPerformanceOverlay; bool checkerboardOffscreenLayers; bool checkerboardRasterCacheImages; @JsonKey(defaultValue: MarianumConnectEndpoint.live) MarianumConnectEndpoint marianumConnectEndpoint; @JsonKey(defaultValue: '') String marianumConnectCustomUrl; /// Optional override for the backend-independent emergency-notice source. /// Empty falls back to [emergencyNoticeDefaultUrl]. @JsonKey(defaultValue: '') String emergencyNoticeUrl; DevToolsSettings({ required this.showPerformanceOverlay, required this.checkerboardOffscreenLayers, required this.checkerboardRasterCacheImages, this.marianumConnectEndpoint = MarianumConnectEndpoint.live, this.marianumConnectCustomUrl = '', this.emergencyNoticeUrl = '', }); // Resolves the effective base URL, falling back to live when the custom URL // is empty or malformed. HTTP custom endpoints are allowed only outside // release builds so a leaked debug URL never ships a plaintext bearer token. String resolveMarianumConnectBaseUrl() { switch (marianumConnectEndpoint) { case MarianumConnectEndpoint.live: return liveUrl; case MarianumConnectEndpoint.beta: return betaUrl; case MarianumConnectEndpoint.custom: final sanitized = sanitizeCustomUrl(marianumConnectCustomUrl); return sanitized ?? liveUrl; } } static const String liveUrl = 'https://connect.marianum-fulda.de'; static const String betaUrl = 'https://connect-beta.marianum-fulda.de'; /// Compiled-in source for the backend-independent emergency notice. Must live /// on infrastructure that is reachable even when MarianumConnect is down. static const String emergencyNoticeDefaultUrl = 'https://www.marianum-fulda.de/~aushang/marMobile.override'; String? resolveEmergencyNoticeUrl() => sanitizeCustomUrl(emergencyNoticeUrl) ?? sanitizeCustomUrl(emergencyNoticeDefaultUrl); /// `true` in builds where plaintext HTTP custom endpoints are still allowed /// (debug, profile). Release builds keep this `false` and the picker /// rejects `http://` entirely. static bool get allowsHttpCustomEndpoint => !kReleaseMode; /// Returns the trimmed URL without a trailing slash, or null when the input /// is not a usable HTTP/HTTPS URL. HTTP is only accepted when /// [allowsHttpCustomEndpoint] is `true` — i.e. outside release builds. static String? sanitizeCustomUrl(String raw) { final trimmed = raw.trim(); if (trimmed.isEmpty) return null; final uri = Uri.tryParse(trimmed); if (uri == null || !uri.hasScheme) return null; if (uri.scheme == 'https') { // always fine } else if (uri.scheme == 'http' && allowsHttpCustomEndpoint) { // dev only } else { return null; } if (uri.host.isEmpty) return null; return trimmed.endsWith('/') ? trimmed.substring(0, trimmed.length - 1) : trimmed; } /// `true` when the configured custom URL is plain HTTP — the picker shows /// this as a warning so developers don't accidentally ship a debug URL. bool get marianumConnectCustomUrlIsInsecure { final sanitized = sanitizeCustomUrl(marianumConnectCustomUrl); return sanitized != null && Uri.parse(sanitized).scheme == 'http'; } factory DevToolsSettings.fromJson(Map json) => _$DevToolsSettingsFromJson(json); Map toJson() => _$DevToolsSettingsToJson(this); }