1
0
forked from baron/baron-sso

i18n 대대적 변경

This commit is contained in:
Lectom C Han
2026-02-10 19:13:00 +09:00
parent 798d37bed9
commit 8df95c8a13
27 changed files with 3910 additions and 594 deletions

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
@@ -33,12 +34,14 @@ class AuditService {
);
if (response.statusCode >= 200 && response.statusCode < 300) {
print("Audit log sent successfully");
debugPrint('Audit log sent successfully');
} else {
print("Failed to send audit log: ${response.statusCode} ${response.body}");
debugPrint(
'Failed to send audit log: ${response.statusCode} ${response.body}',
);
}
} catch (e) {
print("Error sending audit log: $e");
debugPrint('Error sending audit log: $e');
}
}
}

View File

@@ -1,6 +1,7 @@
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 'web_window.dart';
import 'auth_token_store.dart';
@@ -30,13 +31,26 @@ class AuthProxyService {
return drySend == true;
}
static Exception _error(String key, String fallback, {String? detail}) {
return Exception(
tr(
key,
fallback: fallback,
params: detail != null ? {'error': detail} : null,
),
);
}
static Future<Map<String, dynamic>> fetchPasswordPolicy() async {
final url = Uri.parse('$_baseUrl/api/v1/auth/password/policy');
final response = await http.get(url);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to fetch password policy');
throw _error(
'err.userfront.auth_proxy.password_policy_fetch',
'비밀번호 정책을 불러오지 못했습니다.',
);
}
}
@@ -52,7 +66,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
}
throw Exception('Failed to load profile: ${response.body}');
throw _error(
'err.userfront.auth_proxy.profile_load',
'프로필을 불러오지 못했습니다: {{error}}',
detail: response.body,
);
} finally {
client.close();
}
@@ -107,7 +125,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to init login: ${response.body}');
throw _error(
'err.userfront.auth_proxy.login_init',
'로그인 초기화에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -128,7 +150,11 @@ class AuthProxyService {
if (response.statusCode == 400) {
return jsonDecode(response.body);
}
throw Exception('Polling failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.login_poll',
'로그인 상태 확인에 실패했습니다: {{error}}',
detail: response.body,
);
}
static Future<Map<String, dynamic>> verifyMagicLink(String token, {bool verifyOnly = false}) async {
@@ -146,7 +172,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Verification failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -176,7 +206,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Verification failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -198,7 +232,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Verification failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.verify_failed',
'검증에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -225,7 +263,13 @@ class AuthProxyService {
return data;
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to login');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.login_failed',
fallback: '로그인에 실패했습니다.',
),
);
}
}
static Future<Map<String, dynamic>> getConsentInfo(String consentChallenge) async {
@@ -239,7 +283,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to get consent info');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.consent_fetch',
fallback: '동의 정보를 가져오지 못했습니다.',
),
);
}
}
@@ -262,7 +312,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to accept consent');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.consent_accept',
fallback: '동의 처리에 실패했습니다.',
),
);
}
}
@@ -282,7 +338,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to reject consent');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.consent_reject',
fallback: '동의 거부에 실패했습니다.',
),
);
}
}
@@ -311,7 +373,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to accept OIDC login');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.oidc_accept',
fallback: 'OIDC 로그인 승인에 실패했습니다.',
),
);
}
} finally {
client.close();
@@ -334,7 +402,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to initiate password reset');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.password_reset_init',
fallback: '비밀번호 재설정을 시작하지 못했습니다.',
),
);
}
}
@@ -361,7 +435,13 @@ class AuthProxyService {
return jsonDecode(response.body);
} else {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? 'Failed to complete password reset');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.password_reset_complete',
fallback: '비밀번호 재설정에 실패했습니다.',
),
);
}
}
@@ -377,7 +457,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to send SMS: ${response.body}');
throw _error(
'err.userfront.auth_proxy.sms_send',
'SMS 전송에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -396,7 +480,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to verify code: ${response.body}');
throw _error(
'err.userfront.auth_proxy.code_verify',
'인증 코드 확인에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -410,7 +498,11 @@ class AuthProxyService {
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to init QR login: ${response.body}');
throw _error(
'err.userfront.auth_proxy.qr_init',
'QR 로그인을 시작하지 못했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -428,7 +520,11 @@ class AuthProxyService {
if (response.statusCode == 400) {
return jsonDecode(response.body);
}
throw Exception('QR Polling failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.qr_poll',
'QR 상태 확인에 실패했습니다: {{error}}',
detail: response.body,
);
}
static Future<void> approveQrLogin(
@@ -462,7 +558,11 @@ class AuthProxyService {
));
if (response.statusCode != 200) {
throw Exception('QR Approval failed: ${response.body}');
throw _error(
'err.userfront.auth_proxy.qr_approve',
'QR 승인에 실패했습니다: {{error}}',
detail: response.body,
);
}
} finally {
client?.close();
@@ -509,7 +609,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to create user: ${response.body}');
throw _error(
'err.userfront.auth_proxy.user_create',
'사용자 생성에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -531,7 +635,11 @@ class AuthProxyService {
final data = jsonDecode(response.body);
return data['users'] ?? [];
} else {
throw Exception('Failed to list users: ${response.body}');
throw _error(
'err.userfront.auth_proxy.user_list',
'사용자 목록 조회에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -548,7 +656,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to delete user: ${response.body}');
throw _error(
'err.userfront.auth_proxy.user_delete',
'사용자 삭제에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -566,7 +678,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to update status: ${response.body}');
throw _error(
'err.userfront.auth_proxy.user_status_update',
'상태 업데이트에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -595,7 +711,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to update user: ${response.body}');
throw _error(
'err.userfront.auth_proxy.user_update',
'사용자 수정에 실패했습니다: {{error}}',
detail: response.body,
);
}
}
@@ -622,7 +742,10 @@ class AuthProxyService {
final data = jsonDecode(response.body);
return data['items'] ?? [];
} else {
throw Exception('연동된 앱 목록을 불러오지 못했습니다.');
throw _error(
'err.userfront.auth_proxy.linked_apps_load',
'연동된 앱 목록을 불러오지 못했습니다.',
);
}
} finally {
client.close();
@@ -650,7 +773,13 @@ class AuthProxyService {
if (response.statusCode != 200) {
final errorBody = jsonDecode(response.body);
throw Exception(errorBody['error'] ?? '연동 해지에 실패했습니다.');
throw Exception(
errorBody['error'] ??
tr(
'err.userfront.auth_proxy.linked_app_revoke',
fallback: '연동 해지에 실패했습니다.',
),
);
}
} finally {
client.close();
@@ -688,7 +817,6 @@ class AuthProxyService {
}
static int _clientLogFailureCount = 0;
static DateTime? _clientLogLastFailureAt;
static DateTime? _clientLogOpenUntil;
static bool _canSendClientLog() {
@@ -702,7 +830,6 @@ class AuthProxyService {
static void _recordClientLogFailure() {
_clientLogFailureCount += 1;
_clientLogLastFailureAt = DateTime.now();
if (_clientLogFailureCount >= 3) {
_clientLogOpenUntil = DateTime.now().add(const Duration(minutes: 1));
_clientLogFailureCount = 0;
@@ -711,7 +838,6 @@ class AuthProxyService {
static void _recordClientLogSuccess() {
_clientLogFailureCount = 0;
_clientLogLastFailureAt = null;
_clientLogOpenUntil = null;
}
@@ -743,7 +869,11 @@ class AuthProxyService {
);
if (response.statusCode != 200) {
throw Exception('Failed to send code: ${response.body}');
throw _error(
'err.userfront.auth_proxy.phone_code_send',
'인증 코드 전송에 실패했습니다: {{error}}',
detail: response.body,
);
}
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:html' as html;
class AuthTokenStore {

View File

@@ -1,6 +1,8 @@
import 'package:flutter/foundation.dart';
void implSendLoginSuccess(String token) {
// No-op on non-web platforms
print("Not on web: Login Success with token: $token");
debugPrint('Not on web: Login Success with token: $token');
}
bool implIsPopup() {

View File

@@ -1,5 +1,8 @@
import 'dart:html' as html;
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:async';
import 'dart:html' as html;
import 'package:flutter/foundation.dart';
void implSendLoginSuccess(String token) {
final message = {'type': 'LOGIN_SUCCESS', 'token': token};
@@ -7,9 +10,9 @@ void implSendLoginSuccess(String token) {
if (html.window.opener != null) {
try {
html.window.opener!.postMessage(message, '*');
print("Sent login success message to opener");
debugPrint('Sent login success message to opener');
} catch (e) {
print("Failed to postMessage: $e");
debugPrint('Failed to postMessage: $e');
}
// Close the popup after a short delay to ensure message sending
@@ -18,7 +21,7 @@ void implSendLoginSuccess(String token) {
});
} else {
// Should not happen given isPopup check, but as fallback:
print("No opener found during popup flow.");
debugPrint('No opener found during popup flow.');
}
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:html' as html;
class WebWindow {