feat: LAN-first development with optional auth
- Default API URLs now use LAN IPs (192.168.86.149) - Auth interceptor skips auth when using LAN endpoints - Production builds override with --dart-define Development: flutter run -d chrome (no auth needed on LAN) Production: flutter build web --dart-define=CORE_API_URL=https://api.schweitz.net 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- API defaults now use LAN IPs for local development (no auth required)
|
||||||
|
- Auth interceptor skips authentication when using LAN endpoints
|
||||||
|
|
||||||
## [0.3.0] - 2025-12-30
|
## [0.3.0] - 2025-12-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import '../auth/auth_provider.dart';
|
import '../auth/auth_provider.dart';
|
||||||
|
import '../config/app_config.dart';
|
||||||
import '../error/app_exception.dart';
|
import '../error/app_exception.dart';
|
||||||
|
|
||||||
/// Adds authentication token to requests.
|
/// Adds authentication token to requests.
|
||||||
|
///
|
||||||
|
/// Skipped entirely when [AppConfig.requiresAuth] is false (LAN development).
|
||||||
class AuthInterceptor extends Interceptor {
|
class AuthInterceptor extends Interceptor {
|
||||||
AuthInterceptor(this._ref);
|
AuthInterceptor(this._ref);
|
||||||
|
|
||||||
@@ -14,6 +17,12 @@ class AuthInterceptor extends Interceptor {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||||
|
// Skip auth for LAN development
|
||||||
|
if (!AppConfig.requiresAuth) {
|
||||||
|
handler.next(options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final authState = _ref.read(authNotifierProvider);
|
final authState = _ref.read(authNotifierProvider);
|
||||||
|
|
||||||
authState.whenData((auth) {
|
authState.whenData((auth) {
|
||||||
@@ -27,6 +36,12 @@ class AuthInterceptor extends Interceptor {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||||
|
// Skip auth error handling for LAN development
|
||||||
|
if (!AppConfig.requiresAuth) {
|
||||||
|
handler.next(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (err.response?.statusCode == 401) {
|
if (err.response?.statusCode == 401) {
|
||||||
// Token expired - trigger re-authentication
|
// Token expired - trigger re-authentication
|
||||||
_ref.read(authNotifierProvider.notifier).signOut();
|
_ref.read(authNotifierProvider.notifier).signOut();
|
||||||
|
|||||||
@@ -1,19 +1,35 @@
|
|||||||
/// Application configuration from compile-time environment variables.
|
/// Application configuration from compile-time environment variables.
|
||||||
///
|
///
|
||||||
/// Set via: flutter build --dart-define=API_URL=https://...
|
/// ## Development (LAN - no auth required)
|
||||||
|
/// Default values use LAN IPs for local development:
|
||||||
|
/// ```bash
|
||||||
|
/// flutter run -d chrome
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Production (public URLs - auth required)
|
||||||
|
/// Override with public URLs for production builds:
|
||||||
|
/// ```bash
|
||||||
|
/// flutter build web \
|
||||||
|
/// --dart-define=CORE_API_URL=https://api.schweitz.net \
|
||||||
|
/// --dart-define=TATLOCK_API_URL=https://tatlock.schweitz.net
|
||||||
|
/// ```
|
||||||
class AppConfig {
|
class AppConfig {
|
||||||
AppConfig._();
|
AppConfig._();
|
||||||
|
|
||||||
/// Core API base URL
|
/// Core API base URL
|
||||||
|
/// - LAN default: No auth required
|
||||||
|
/// - Production: https://api.schweitz.net (requires OIDC)
|
||||||
static const coreApiUrl = String.fromEnvironment(
|
static const coreApiUrl = String.fromEnvironment(
|
||||||
'CORE_API_URL',
|
'CORE_API_URL',
|
||||||
defaultValue: 'https://api.schweitz.net',
|
defaultValue: 'http://192.168.86.149:8083',
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Tatlock API base URL
|
/// Tatlock API base URL
|
||||||
|
/// - LAN default: No auth required
|
||||||
|
/// - Production: https://tatlock.schweitz.net (requires OIDC)
|
||||||
static const tatlockApiUrl = String.fromEnvironment(
|
static const tatlockApiUrl = String.fromEnvironment(
|
||||||
'TATLOCK_API_URL',
|
'TATLOCK_API_URL',
|
||||||
defaultValue: 'https://tatlock.schweitz.net',
|
defaultValue: 'http://192.168.86.149:8000',
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Authentik OIDC discovery URL
|
/// Authentik OIDC discovery URL
|
||||||
@@ -37,4 +53,9 @@ class AppConfig {
|
|||||||
|
|
||||||
/// Whether running in debug mode
|
/// Whether running in debug mode
|
||||||
static const isDebug = bool.fromEnvironment('DEBUG', defaultValue: false);
|
static const isDebug = bool.fromEnvironment('DEBUG', defaultValue: false);
|
||||||
|
|
||||||
|
/// Whether auth is required (false for LAN development)
|
||||||
|
static bool get requiresAuth =>
|
||||||
|
coreApiUrl.contains('schweitz.net') ||
|
||||||
|
tatlockApiUrl.contains('schweitz.net');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user