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

View File

@@ -1,41 +1,16 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:userfront/i18n.dart';
import 'http_client.dart';
import 'auth_token_store.dart';
import 'log_policy.dart';
import 'runtime_env.dart';
class AuthProxyService {
static 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;
}
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 String get _baseUrl => runtimeBackendUrl();
static bool get _isProd {
final env = _envOrDefault('APP_ENV', 'dev').toLowerCase();
final env = envOrDefault('APP_ENV', 'dev').toLowerCase();
return LogPolicy.isProductionEnv(env);
}
@@ -156,7 +131,7 @@ class AuthProxyService {
bool? drySend,
}) async {
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};
if (_shouldSendDrySend(drySend)) {
@@ -897,10 +872,10 @@ class AuthProxyService {
if (!_canSendClientLog()) {
return;
}
final appEnv = _envOrDefault('APP_ENV', 'dev');
final productionDebugFlag = _envOrDefault(
final appEnv = envOrDefault('APP_ENV', 'dev');
final productionDebugFlag = envOrDefault(
'CLIENT_LOG_DEBUG',
_envOrDefault('USERFRONT_DEBUG_LOG', ''),
envOrDefault('USERFRONT_DEBUG_LOG', ''),
);
if (!LogPolicy.shouldRelayClientLog(
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 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/auth_token_store.dart';
import '../../../../core/services/http_client.dart';
import '../../../../core/services/runtime_env.dart';
import 'package:userfront/i18n.dart';
import 'models.dart';
String _envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
String get _baseUrl => _envOrDefault('BACKEND_URL', 'https://sso.hmac.kr');
String get _baseUrl => runtimeBackendUrl();
Future<AuditPage> _fetchAuthTimelinePage({String? cursor}) async {
final queryParameters = <String, String>{'limit': '20'};

View File

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

View File

@@ -1,11 +1,11 @@
import 'dart:convert';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/services/auth_proxy_service.dart';
import '../../../../core/services/auth_token_store.dart';
import '../../../../core/services/http_client.dart';
import '../../../../core/services/runtime_env.dart';
import '../models.dart';
class UserSessionsNotifier extends AsyncNotifier<List<UserSessionSummary>> {
@@ -14,15 +14,8 @@ class UserSessionsNotifier extends AsyncNotifier<List<UserSessionSummary>> {
return _fetchSessions();
}
String _envOrDefault(String key, String fallback) {
if (!dotenv.isInitialized) {
return fallback;
}
return dotenv.env[key] ?? fallback;
}
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 useCookie = AuthTokenStore.usesCookie();

View File

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

View File

@@ -3,6 +3,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:easy_localization/easy_localization.dart' hide tr;
@@ -161,10 +162,20 @@ bool _shouldRunStartupSessionRecovery(Uri 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 {
WidgetsFlutterBinding.ensureInitialized();
usePathUrlStrategy();
await EasyLocalization.ensureInitialized();
await _loadRuntimeEnv();
LocaleRegistry.primeWithDefaults();
// 1. Global Error Handling

View File

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