Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ jobs:
flutter build apk --release --flavor prod \
--build-name="${{ github.ref_name }}" \
--build-number="${{ github.run_number }}" \
--dart-define=BACKEND_BASE_URL="${{ vars.BACKEND_BASE_URL || 'https://api.schuly.dev' }}" \
--dart-define=SENTRY_DSN="$SENTRY_DSN" \
--dart-define=SENTRY_RELEASE="${{ github.ref_name }}" \
--dart-define=SENTRY_ENVIRONMENT="production"
Expand All @@ -88,6 +89,7 @@ jobs:
flutter build appbundle --release --flavor prod \
--build-name="${{ github.ref_name }}" \
--build-number="${{ github.run_number }}" \
--dart-define=BACKEND_BASE_URL="${{ vars.BACKEND_BASE_URL || 'https://api.schuly.dev' }}" \
--dart-define=SENTRY_DSN="$SENTRY_DSN" \
--dart-define=SENTRY_RELEASE="${{ github.ref_name }}" \
--dart-define=SENTRY_ENVIRONMENT="production"
Expand Down Expand Up @@ -134,6 +136,7 @@ jobs:
flutter build ios --release --no-codesign \
--build-name="${{ github.ref_name }}" \
--build-number="${{ github.run_number }}" \
--dart-define=BACKEND_BASE_URL="${{ vars.BACKEND_BASE_URL || 'https://api.schuly.dev' }}" \
--dart-define=SENTRY_DSN="$SENTRY_DSN" \
--dart-define=SENTRY_RELEASE="${{ github.ref_name }}" \
--dart-define=SENTRY_ENVIRONMENT="production"
Expand Down
14 changes: 14 additions & 0 deletions lib/config/backend_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ class BackendConfig {
static String normalise(String? value) =>
(value ?? '').trim().replaceAll(RegExp(r'/+$'), '');

/// True if [value] uses plaintext `http://` to a non-loopback host, so
/// credentials and tokens would travel in the clear. Used to warn before a
/// self-hoster saves an insecure custom backend.
static bool isInsecure(String? value) {
final uri = Uri.tryParse(normalise(value));
if (uri == null || uri.scheme != 'http') return false;
final h = uri.host.toLowerCase();
return h.isNotEmpty &&
h != 'localhost' &&
h != '127.0.0.1' &&
h != '::1' &&
!h.endsWith('.localhost');
}

/// Probes [baseUrl] by fetching the anonymous `GET /api/app`. A reachable
/// Schuly backend returns a JSON object with a `clientId`; on success this
/// returns its reported `version` (or `'unknown'` if the field is missing).
Expand Down
8 changes: 8 additions & 0 deletions lib/ui/onboarding/onboarding_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,14 @@ class _ServerPage extends StatelessWidget {
hint: 'https://schuly.example.com',
autocorrect: false,
),
if (BackendConfig.isInsecure(urlController.text)) ...[
const SizedBox(height: 8),
Text(
'Plaintext http:// - your login would be sent unencrypted. Use https:// unless this is a trusted local network.',
textAlign: TextAlign.center,
style: typography.xs.copyWith(color: colors.error),
),
],
],
if (error != null) ...[
const SizedBox(height: 12),
Expand Down
14 changes: 14 additions & 0 deletions lib/ui/settings/settings_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ class _ServerDialogState extends State<_ServerDialog> {
String? _error;
bool _busy = false;

@override
void initState() {
super.initState();
// Live-update the insecure-URL warning as the user types.
_urlCtrl.addListener(() => setState(() {}));
}

@override
void dispose() {
_urlCtrl.dispose();
Expand Down Expand Up @@ -230,6 +237,13 @@ class _ServerDialogState extends State<_ServerDialog> {
hint: 'https://schuly.example.com',
autocorrect: false,
),
if (BackendConfig.isInsecure(_urlCtrl.text)) ...[
const SizedBox(height: 6),
Text(
'Plaintext http:// - your login would be sent unencrypted. Use https:// unless this is a trusted local network.',
style: typography.xs.copyWith(color: colors.error),
),
],
],
if (_error != null) ...[
const SizedBox(height: 8),
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"description": "Task runner for the Schuly Flutter app. Use `bun run <script>`.",
"scripts": {
"dev": "flutter run --flavor dev",
"prod": "flutter run --flavor prod",
"prod": "flutter run --flavor prod --dart-define=BACKEND_BASE_URL=https://api.schuly.dev",
"analyze": "flutter analyze",
"test": "flutter test",
"format": "dart format lib",
"build:apk:dev": "flutter build apk --flavor dev --release",
"build:apk:prod": "flutter build apk --flavor prod --release",
"build:ios": "flutter build ios --flavor prod --no-codesign",
"build:apk:prod": "flutter build apk --flavor prod --release --dart-define=BACKEND_BASE_URL=https://api.schuly.dev",
"build:ios": "flutter build ios --flavor prod --no-codesign --dart-define=BACKEND_BASE_URL=https://api.schuly.dev",
"install:dev": "flutter build apk --flavor dev --release && flutter install --flavor dev",
"install:prod": "flutter build apk --flavor prod --release && flutter install --flavor prod",
"install:prod": "flutter build apk --flavor prod --release --dart-define=BACKEND_BASE_URL=https://api.schuly.dev && flutter install --flavor prod",
"adb:reverse": "adb reverse tcp:5033 tcp:5033",
"install:dev:usb": "bun run adb:reverse && bun run install:dev",
"build:apk:dev:url": "flutter build apk --flavor dev --release --dart-define=BACKEND_BASE_URL=$BACKEND_BASE_URL",
Expand Down
Loading