1
0
forked from baron/baron-sso

userfront 런타임 BACKEND_URL fallback 수정

This commit is contained in:
2026-05-15 17:57:47 +09:00
parent eddab895e9
commit cd16cb3a4a
9 changed files with 64 additions and 79 deletions

View File

@@ -1,18 +1,10 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'runtime_env.dart';
class AuditService { class AuditService {
static String _envOrDefault(String key, String fallback) { static String get _baseUrl => runtimeBackendUrl();
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
static String get _baseUrl =>
_envOrDefault('BACKEND_URL', 'https://sso.hmac.kr');
static Future<void> logEvent({ static Future<void> logEvent({
required String userId, required String userId,

View File

@@ -1,41 +1,16 @@
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:userfront/i18n.dart'; import 'package:userfront/i18n.dart';
import 'http_client.dart'; import 'http_client.dart';
import 'auth_token_store.dart'; import 'auth_token_store.dart';
import 'log_policy.dart'; import 'log_policy.dart';
import 'runtime_env.dart';
class AuthProxyService { class AuthProxyService {
static String _envOrDefault(String key, String fallback) { static String get _baseUrl => runtimeBackendUrl();
if (!dotenv.isInitialized) {
return fallback;
}
final value = dotenv.env[key];
if (value == null || value.trim().isEmpty) {
return fallback;
}
return value;
}
static String _fallbackOrigin() {
try {
final origin = Uri.base.origin;
if (origin.isNotEmpty && origin != 'null') {
return origin;
}
} catch (_) {}
return 'http://sso.hmac.kr';
}
static String get _baseUrl {
final rawUrl = _envOrDefault('BACKEND_URL', _fallbackOrigin());
// 배포 환경에서 $ 기호나 공백이 섞여 들어오는 경우를 방지하기 위해 정제합니다.
return rawUrl.replaceAll(r'$', '').trim().replaceAll(RegExp(r'/$'), '');
}
static bool get _isProd { static bool get _isProd {
final env = _envOrDefault('APP_ENV', 'dev').toLowerCase(); final env = envOrDefault('APP_ENV', 'dev').toLowerCase();
return LogPolicy.isProductionEnv(env); return LogPolicy.isProductionEnv(env);
} }
@@ -156,7 +131,7 @@ class AuthProxyService {
bool? drySend, bool? drySend,
}) async { }) async {
final url = Uri.parse('$_baseUrl/api/v1/auth/enchanted-link/init'); final url = Uri.parse('$_baseUrl/api/v1/auth/enchanted-link/init');
final userfrontUrl = _envOrDefault('USERFRONT_URL', _fallbackOrigin()); final userfrontUrl = runtimeUserfrontUrl();
final body = <String, dynamic>{'loginId': loginId, 'uri': userfrontUrl}; final body = <String, dynamic>{'loginId': loginId, 'uri': userfrontUrl};
if (_shouldSendDrySend(drySend)) { if (_shouldSendDrySend(drySend)) {
@@ -897,10 +872,10 @@ class AuthProxyService {
if (!_canSendClientLog()) { if (!_canSendClientLog()) {
return; return;
} }
final appEnv = _envOrDefault('APP_ENV', 'dev'); final appEnv = envOrDefault('APP_ENV', 'dev');
final productionDebugFlag = _envOrDefault( final productionDebugFlag = envOrDefault(
'CLIENT_LOG_DEBUG', 'CLIENT_LOG_DEBUG',
_envOrDefault('USERFRONT_DEBUG_LOG', ''), envOrDefault('USERFRONT_DEBUG_LOG', ''),
); );
if (!LogPolicy.shouldRelayClientLog( if (!LogPolicy.shouldRelayClientLog(
level: level, level: level,

View File

@@ -0,0 +1,34 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
String runtimeOriginFallback() {
try {
final origin = Uri.base.origin;
if (origin.isNotEmpty && origin != 'null') {
return origin;
}
} catch (_) {}
return 'https://sso-test.hmac.kr';
}
String envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
final value = dotenv.env[key];
if (value == null || value.trim().isEmpty) {
return fallback;
}
return value;
}
String sanitizedUrl(String value) {
return value.replaceAll(r'$', '').trim().replaceAll(RegExp(r'/$'), '');
}
String runtimeBackendUrl() {
return sanitizedUrl(envOrDefault('BACKEND_URL', runtimeOriginFallback()));
}
String runtimeUserfrontUrl() {
return sanitizedUrl(envOrDefault('USERFRONT_URL', runtimeOriginFallback()));
}

View File

@@ -1,21 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/auth_token_store.dart'; import '../../../../core/services/auth_token_store.dart';
import '../../../../core/services/http_client.dart'; import '../../../../core/services/http_client.dart';
import '../../../../core/services/runtime_env.dart';
import 'package:userfront/i18n.dart'; import 'package:userfront/i18n.dart';
import 'models.dart'; import 'models.dart';
String _envOrDefault(String key, String fallback) { String get _baseUrl => runtimeBackendUrl();
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
String get _baseUrl => _envOrDefault('BACKEND_URL', 'https://sso.hmac.kr');
Future<AuditPage> _fetchAuthTimelinePage({String? cursor}) async { Future<AuditPage> _fetchAuthTimelinePage({String? cursor}) async {
final queryParameters = <String, String>{'limit': '20'}; final queryParameters = <String, String>{'limit': '20'};

View File

@@ -1,9 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:userfront/core/services/auth_proxy_service.dart'; import 'package:userfront/core/services/auth_proxy_service.dart';
import 'package:userfront/core/services/auth_token_store.dart'; import 'package:userfront/core/services/auth_token_store.dart';
import 'package:userfront/core/services/http_client.dart'; import 'package:userfront/core/services/http_client.dart';
import 'package:userfront/core/services/runtime_env.dart';
class LinkedRp { class LinkedRp {
final String id; final String id;
@@ -62,16 +62,9 @@ class LinkedRpsNotifier extends AsyncNotifier<List<LinkedRp>> {
return _fetchLinkedRps(); return _fetchLinkedRps();
} }
String _envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
Future<List<LinkedRp>> _fetchLinkedRps() async { Future<List<LinkedRp>> _fetchLinkedRps() async {
try { try {
final baseUrl = _envOrDefault('BACKEND_URL', 'https://sso.hmac.kr'); final baseUrl = runtimeBackendUrl();
final url = Uri.parse('$baseUrl/api/v1/user/rp/linked'); final url = Uri.parse('$baseUrl/api/v1/user/rp/linked');
final useCookie = AuthTokenStore.usesCookie(); final useCookie = AuthTokenStore.usesCookie();

View File

@@ -1,11 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/auth_proxy_service.dart'; import '../../../../core/services/auth_proxy_service.dart';
import '../../../../core/services/auth_token_store.dart'; import '../../../../core/services/auth_token_store.dart';
import '../../../../core/services/http_client.dart'; import '../../../../core/services/http_client.dart';
import '../../../../core/services/runtime_env.dart';
import '../models.dart'; import '../models.dart';
class UserSessionsNotifier extends AsyncNotifier<List<UserSessionSummary>> { class UserSessionsNotifier extends AsyncNotifier<List<UserSessionSummary>> {
@@ -14,15 +14,8 @@ class UserSessionsNotifier extends AsyncNotifier<List<UserSessionSummary>> {
return _fetchSessions(); return _fetchSessions();
} }
String _envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
Future<List<UserSessionSummary>> _fetchSessions() async { Future<List<UserSessionSummary>> _fetchSessions() async {
final baseUrl = _envOrDefault('BACKEND_URL', 'https://sso.hmac.kr'); final baseUrl = runtimeBackendUrl();
final url = Uri.parse('$baseUrl/api/v1/user/sessions'); final url = Uri.parse('$baseUrl/api/v1/user/sessions');
final useCookie = AuthTokenStore.usesCookie(); final useCookie = AuthTokenStore.usesCookie();

View File

@@ -1,20 +1,12 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:userfront/i18n.dart'; import 'package:userfront/i18n.dart';
import '../models/user_profile_model.dart'; import '../models/user_profile_model.dart';
import '../../../../core/services/auth_token_store.dart'; import '../../../../core/services/auth_token_store.dart';
import '../../../../core/services/http_client.dart'; import '../../../../core/services/http_client.dart';
import '../../../../core/services/runtime_env.dart';
class ProfileRepository { class ProfileRepository {
static String _envOrDefault(String key, String fallback) { static String get _baseUrl => runtimeBackendUrl();
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
static String get _baseUrl =>
_envOrDefault('BACKEND_URL', 'https://sso.hmac.kr');
// Helper to get session token // Helper to get session token
static Future<String?> _getToken() async { static Future<String?> _getToken() async {

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:easy_localization/easy_localization.dart' hide tr; import 'package:easy_localization/easy_localization.dart' hide tr;
@@ -161,10 +162,20 @@ bool _shouldRunStartupSessionRecovery(Uri uri) {
return !isPublicAuthPath(path, uri); return !isPublicAuthPath(path, uri);
} }
Future<void> _loadRuntimeEnv() async {
for (final fileName in const ['assets/.env', '.env']) {
try {
await dotenv.load(fileName: fileName);
return;
} catch (_) {}
}
}
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
usePathUrlStrategy(); usePathUrlStrategy();
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
await _loadRuntimeEnv();
LocaleRegistry.primeWithDefaults(); LocaleRegistry.primeWithDefaults();
// 1. Global Error Handling // 1. Global Error Handling

View File

@@ -21,6 +21,8 @@ log_format json_combined escape=json
server { server {
listen 5000; listen 5000;
absolute_redirect off;
port_in_redirect off;
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
include /etc/nginx/mime.types; include /etc/nginx/mime.types;