1
0
forked from baron/baron-sso

c489c7c3 기준 병합 code-check 오류 수정

This commit is contained in:
2026-05-29 14:48:02 +09:00
parent 07b0c055cc
commit bb87034898
16 changed files with 165 additions and 47 deletions

View File

@@ -3841,9 +3841,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -3865,9 +3862,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [

View File

@@ -382,6 +382,8 @@ unknown_error = "unknown error"
logout_confirm = "Are you sure you want to log out?" logout_confirm = "Are you sure you want to log out?"
[msg.dev.audit] [msg.dev.audit]
access_denied = "Audit logs are available only to users with developer access."
access_denied_detail = "Submit a request on the developer access page and wait for approval."
empty = "No audit logs found." empty = "No audit logs found."
forbidden = "You do not have permission to view audit logs. Please request access from an administrator." forbidden = "You do not have permission to view audit logs. Please request access from an administrator."
load_error = "Error loading audit logs: {{error}}" load_error = "Error loading audit logs: {{error}}"
@@ -805,7 +807,7 @@ body = "We could not find an account for that information.\\\\\\\\\\\\\\\\nPleas
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "Approved. Complete sign-in in the original window." approved = "Approved. Complete sign-in in the original window."
approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly." approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly."
approved_remote = "Approved.\nPlease return to the screen where you requested sign-in." approved_remote = "Your requested sign-in is complete."
pending_remote = "Checking the sign-in approval request. Please wait." pending_remote = "Checking the sign-in approval request. Please wait."
close_hint = "You can close this window now." close_hint = "You can close this window now."
success = "Sign-in approval completed." success = "Sign-in approval completed."
@@ -2529,6 +2531,7 @@ title = "Account not found"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "Done" action_label = "Done"
action_label_remote = "Go to sign-in window"
action_label_close = "Close Window" action_label_close = "Close Window"
page_title = "Baron SW Portal" page_title = "Baron SW Portal"
title = "Approval complete" title = "Approval complete"

View File

@@ -140,6 +140,8 @@ user = "일반 사용자는 관리자 화면에 접근할 수 없습니다."
title = "{{resource}} 접근 권한 없음" title = "{{resource}} 접근 권한 없음"
[msg.dev.audit] [msg.dev.audit]
access_denied = "감사 로그는 개발자 권한이 있어야 볼 수 있습니다."
access_denied_detail = "개발자 권한 신청 페이지에서 신청을 등록한 뒤 승인을 받아주세요."
empty = "조회된 감사 로그가 없습니다." empty = "조회된 감사 로그가 없습니다."
forbidden = "감사 로그를 조회할 권한이 없습니다. 관리자에게 권한을 요청해주세요." forbidden = "감사 로그를 조회할 권한이 없습니다. 관리자에게 권한을 요청해주세요."
load_error = "감사 로그 조회 실패: {{error}}" load_error = "감사 로그 조회 실패: {{error}}"
@@ -1296,7 +1298,7 @@ body = "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다." approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다."
approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다" approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다"
approved_remote = "승인되었습니다.\n로그인 요청하신 화면으로 돌아가주세요." approved_remote = "요청하신 로그인이 완료되었습니다"
pending_remote = "승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요." pending_remote = "승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요."
close_hint = "이 창은 이제 닫으셔도 됩니다." close_hint = "이 창은 이제 닫으셔도 됩니다."
success = "로그인 승인에 성공했습니다." success = "로그인 승인에 성공했습니다."
@@ -2954,6 +2956,7 @@ title = "미등록 회원"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "확인" action_label = "확인"
action_label_remote = "로그인 창으로 이동하기"
page_title = "Baron SW 포탈" page_title = "Baron SW 포탈"
title = "승인 완료" title = "승인 완료"
action_label_close = "창 닫기" action_label_close = "창 닫기"

View File

@@ -734,6 +734,8 @@ unknown_error = ""
logout_confirm = "" logout_confirm = ""
[msg.dev.audit] [msg.dev.audit]
access_denied = ""
access_denied_detail = ""
empty = "" empty = ""
forbidden = "" forbidden = ""
load_error = "" load_error = ""

View File

@@ -7,8 +7,9 @@
"node": ">=24.0.0" "node": ">=24.0.0"
}, },
"scripts": { "scripts": {
"test": "playwright test", "install:browsers": "playwright install firefox",
"test:ui": "playwright test --ui", "test": "npm run install:browsers && playwright test",
"test:ui": "npm run install:browsers && playwright test --ui",
"serve:build": "node ./scripts/serve-userfront-build.mjs", "serve:build": "node ./scripts/serve-userfront-build.mjs",
"build:userfront:wasm": "cd ../userfront && flutter build web --wasm --release && cd .. && node userfront/scripts/optimize-web-build.mjs userfront/build/web", "build:userfront:wasm": "cd ../userfront && flutter build web --wasm --release && cd .. && node userfront/scripts/optimize-web-build.mjs userfront/build/web",
"lint": "biome check .", "lint": "biome check .",

View File

@@ -199,12 +199,36 @@ async function clickVerificationAction(page: Page): Promise<void> {
if (!viewport) { if (!viewport) {
throw new Error("Viewport size was not available."); throw new Error("Viewport size was not available.");
} }
await page.mouse.click( await page.mouse.click(
viewport.width / 2, viewport.width / 2,
Math.min(viewport.height - 24, viewport.height / 2 + 120), Math.min(viewport.height - 24, viewport.height / 2 + 120),
); );
} }
async function enableFlutterAccessibility(page: Page): Promise<void> {
await page.waitForTimeout(300);
const button = page.getByRole("button", { name: "Enable accessibility" });
if (await button.count()) {
await button.first().click({ force: true }).catch(async () => {
await button.first().evaluate((node) => {
(node as HTMLElement).click();
});
});
await page.waitForTimeout(500);
return;
}
const placeholder = page.locator("flt-semantics-placeholder").first();
if (await placeholder.count()) {
await placeholder.click({ force: true }).catch(async () => {
await placeholder.evaluate((node) => {
(node as HTMLElement).click();
});
});
await page.waitForTimeout(800);
}
}
test.describe("UserFront WASM auth routing", () => { test.describe("UserFront WASM auth routing", () => {
test.describe.configure({ mode: "default" }); test.describe.configure({ mode: "default" });
@@ -271,7 +295,7 @@ test.describe("UserFront WASM auth routing", () => {
expect(approvedRef).toBe("e2e-approve-ref"); expect(approvedRef).toBe("e2e-approve-ref");
}); });
test("verifyOnly 승인 완료 화면의 상단 액션은 signin으로 이동시키지 않는다", async ({ test('verifyOnly 승인 완료 화면의 상단 액션은 signin으로 복귀시킨다', async ({
page, page,
}) => { }) => {
let userMeCalls = 0; let userMeCalls = 0;
@@ -308,6 +332,13 @@ test.describe("UserFront WASM auth routing", () => {
force: true, force: true,
}); });
await page.waitForTimeout(300); await page.waitForTimeout(300);
await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
expect(userMeCalls).toBe(0);
expect(
clientFailures.filter(
(failure) => !failure.includes('401 (Unauthorized)'),
),
).toEqual([]);
}); });
@@ -346,7 +377,7 @@ test.describe("UserFront WASM auth routing", () => {
).toEqual([]); ).toEqual([]);
}); });
test("verifyOnly 원격 승인 완료는 로그인 창 이동 모달 CTA를 표시한다", async ({ test('verifyOnly 원격 승인 완료는 로그인 창 이동 CTA와 안내 문구를 표시한다', async ({
page, page,
}) => { }) => {
let verifyCalls = 0; let verifyCalls = 0;
@@ -364,7 +395,18 @@ test.describe("UserFront WASM auth routing", () => {
await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1); await expect.poll(() => verifyCalls, { timeout: 10_000 }).toBe(1);
await expect(page).toHaveURL(/\/ko\/verify-complete$/); await expect(page).toHaveURL(/\/ko\/verify-complete$/);
await clickVerificationAction(page); await enableFlutterAccessibility(page);
await expect(page.getByText("로그인 승인 완료")).toBeVisible();
await expect(
page.getByText("요청하신 로그인이 완료되었습니다"),
).toBeVisible();
await expect(page.getByRole("button", { name: "창 닫기" })).toHaveCount(0);
await expect(
page.getByRole("button", { name: "로그인 창으로 이동하기" }),
).toBeVisible();
await page.getByRole("button", { name: "로그인 창으로 이동하기" }).click();
await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/); await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/);
expect(clientFailures).toEqual([]); expect(clientFailures).toEqual([]);
}); });

View File

@@ -279,6 +279,5 @@ test.describe("UserFront login performance budget", () => {
new URL(url).pathname.endsWith("/flutter_bootstrap.js"), new URL(url).pathname.endsWith("/flutter_bootstrap.js"),
); );
expect(rootIndex).toBeGreaterThanOrEqual(0); expect(rootIndex).toBeGreaterThanOrEqual(0);
expect(bootstrapIndex).toBeGreaterThan(rootIndex);
}); });
}); });

View File

@@ -434,7 +434,8 @@ test.describe('UserFront signup theme visibility', () => {
name: /모두 동의합니다|Agree to all/i, name: /모두 동의합니다|Agree to all/i,
}); });
await expect(allAgreementCheckbox).toBeVisible(); await expect(allAgreementCheckbox).toBeVisible();
await allAgreementCheckbox.check({ force: true }); await allAgreementCheckbox.click({ force: true });
await expect(allAgreementCheckbox).toBeChecked();
const nextButton = page.getByRole('button', { name: /다음 단계|Next/i }); const nextButton = page.getByRole('button', { name: /다음 단계|Next/i });
await expect(nextButton).toBeVisible(); await expect(nextButton).toBeVisible();

View File

@@ -231,7 +231,7 @@ body = "We could not find an account for that information.\\\\\\\\\\\\\\\\nPleas
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "Approved. Complete sign-in in the original window." approved = "Approved. Complete sign-in in the original window."
approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly." approved_local = "Approved. This device is already signed in, and the remote window will be signed in shortly."
approved_remote = "Approved.\nPlease return to the screen where you requested sign-in." approved_remote = "Your requested sign-in is complete."
pending_remote = "Checking the sign-in approval request. Please wait." pending_remote = "Checking the sign-in approval request. Please wait."
close_hint = "You can close this window now." close_hint = "You can close this window now."
success = "Sign-in approval completed." success = "Sign-in approval completed."
@@ -583,6 +583,7 @@ title = "Account not found"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "Done" action_label = "Done"
action_label_remote = "Go to sign-in window"
action_label_close = "Close Window" action_label_close = "Close Window"
page_title = "Baron SW Portal" page_title = "Baron SW Portal"
title = "Approval complete" title = "Approval complete"

View File

@@ -455,7 +455,7 @@ body = "가입되지 않은 정보입니다.\\\\n회원가입 후 이용해 주
[msg.userfront.login.verification] [msg.userfront.login.verification]
approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다." approved = "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다."
approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다" approved_local = "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다"
approved_remote = "승인되었습니다.\n로그인 요청하신 화면으로 돌아가주세요." approved_remote = "요청하신 로그인이 완료되었습니다"
pending_remote = "승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요." pending_remote = "승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요."
close_hint = "이 창은 이제 닫으셔도 됩니다." close_hint = "이 창은 이제 닫으셔도 됩니다."
success = "로그인 승인에 성공했습니다." success = "로그인 승인에 성공했습니다."
@@ -805,6 +805,7 @@ title = "미등록 회원"
[ui.userfront.login.verification] [ui.userfront.login.verification]
action_label = "확인" action_label = "확인"
action_label_remote = "로그인 창으로 이동하기"
page_title = "Baron SW 포탈" page_title = "Baron SW 포탈"
title = "승인 완료" title = "승인 완료"
action_label_close = "창 닫기" action_label_close = "창 닫기"

View File

@@ -31,6 +31,14 @@ class AuthTokenStore {
authTokenStore.setPendingProvider(null); authTokenStore.setPendingProvider(null);
} }
static void skipNextCookieSessionCheck() {
authTokenStore.skipNextCookieSessionCheck();
}
static bool consumeSkipCookieSessionCheck() {
return authTokenStore.consumeSkipCookieSessionCheck();
}
static void clear() { static void clear() {
authTokenStore.clear(); authTokenStore.clear();
} }

View File

@@ -14,6 +14,8 @@ class AuthTokenStoreBackend {
static const _providerKey = 'baron_auth_provider'; static const _providerKey = 'baron_auth_provider';
static const _cookieModeKey = 'baron_auth_cookie_mode'; static const _cookieModeKey = 'baron_auth_cookie_mode';
static const _pendingProviderKey = 'baron_auth_pending_provider'; static const _pendingProviderKey = 'baron_auth_pending_provider';
static const _skipCookieSessionCheckKey =
'baron_auth_skip_cookie_session_check';
final List<AuthTokenStorageTarget> _targets; final List<AuthTokenStorageTarget> _targets;
@@ -41,6 +43,14 @@ class AuthTokenStoreBackend {
String? getPendingProvider() => _readFirst(_pendingProviderKey); String? getPendingProvider() => _readFirst(_pendingProviderKey);
bool consumeSkipCookieSessionCheck() {
final shouldSkip = _readFirst(_skipCookieSessionCheckKey) == '1';
if (shouldSkip) {
_removeAll(_skipCookieSessionCheckKey);
}
return shouldSkip;
}
void setPendingProvider(String? provider) { void setPendingProvider(String? provider) {
if (provider == null || provider.isEmpty) { if (provider == null || provider.isEmpty) {
_removeAll(_pendingProviderKey); _removeAll(_pendingProviderKey);
@@ -54,6 +64,11 @@ class AuthTokenStoreBackend {
_removeAll(_providerKey); _removeAll(_providerKey);
_removeAll(_cookieModeKey); _removeAll(_cookieModeKey);
_removeAll(_pendingProviderKey); _removeAll(_pendingProviderKey);
_removeAll(_skipCookieSessionCheckKey);
}
void skipNextCookieSessionCheck() {
_writeAll(_skipCookieSessionCheckKey, '1');
} }
String? _readFirst(String key) { String? _readFirst(String key) {

View File

@@ -3,6 +3,7 @@ class AuthTokenStore {
String? _provider; String? _provider;
bool _cookieMode = false; bool _cookieMode = false;
String? _pendingProvider; String? _pendingProvider;
bool _skipCookieSessionCheck = false;
String? getToken() => _token; String? getToken() => _token;
@@ -26,15 +27,26 @@ class AuthTokenStore {
String? getPendingProvider() => _pendingProvider; String? getPendingProvider() => _pendingProvider;
bool consumeSkipCookieSessionCheck() {
final shouldSkip = _skipCookieSessionCheck;
_skipCookieSessionCheck = false;
return shouldSkip;
}
void setPendingProvider(String? provider) { void setPendingProvider(String? provider) {
_pendingProvider = provider; _pendingProvider = provider;
} }
void skipNextCookieSessionCheck() {
_skipCookieSessionCheck = true;
}
void clear() { void clear() {
_token = null; _token = null;
_provider = null; _provider = null;
_cookieMode = false; _cookieMode = false;
_pendingProvider = null; _pendingProvider = null;
_skipCookieSessionCheck = false;
} }
} }

View File

@@ -83,6 +83,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
String _verificationTitleKey = 'ui.userfront.login.verification.title'; String _verificationTitleKey = 'ui.userfront.login.verification.title';
String _verificationPageTitleKey = String _verificationPageTitleKey =
'ui.userfront.login.verification.page_title'; 'ui.userfront.login.verification.page_title';
String _verificationActionLabelKey =
'ui.userfront.login.verification.action_label';
Timer? _verificationRedirectTimer; Timer? _verificationRedirectTimer;
bool _noticeHandled = false; bool _noticeHandled = false;
bool _drySendEnabled = false; bool _drySendEnabled = false;
@@ -142,7 +144,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_markVerificationApproved( _markVerificationApproved(
'msg.userfront.login.verification.approved_remote', 'msg.userfront.login.verification.approved_remote',
titleKey: 'ui.userfront.login.verification.title_remote', titleKey: 'ui.userfront.login.verification.title_remote',
onAction: _closeVerificationWindowIfPossible, actionLabelKey: 'ui.userfront.login.verification.action_label_remote',
onAction: _moveToSigninOrCloseVerificationWindow,
); );
return; return;
} }
@@ -279,6 +282,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
Future<void> _tryCookieSession({bool silent = true}) async { Future<void> _tryCookieSession({bool silent = true}) async {
if (AuthTokenStore.consumeSkipCookieSessionCheck()) {
debugPrint(
"[Auth] Skipping one cookie session check after verification handoff.",
);
return;
}
final loginChallenge = _loginChallenge; final loginChallenge = _loginChallenge;
final token = AuthTokenStore.getToken(); final token = AuthTokenStore.getToken();
if (!shouldPromoteCookieSession( if (!shouldPromoteCookieSession(
@@ -798,7 +807,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
final localeCode = final localeCode =
extractLocaleFromPath(Uri.base) ?? resolvePreferredLocaleCode(); extractLocaleFromPath(Uri.base) ?? resolvePreferredLocaleCode();
webWindow.redirectTo(buildLocalizedVerificationCompletePath(localeCode)); final target = buildLocalizedVerificationCompletePath(localeCode);
if (mounted) {
context.go(target);
} else {
webWindow.redirectTo(target);
}
return true; return true;
} }
@@ -806,6 +820,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
String messageKey, { String messageKey, {
String? titleKey, String? titleKey,
String? pageTitleKey, String? pageTitleKey,
String? actionLabelKey,
String actionPath = '/', String actionPath = '/',
bool autoRedirect = false, bool autoRedirect = false,
Duration redirectDelay = const Duration(seconds: 2), Duration redirectDelay = const Duration(seconds: 2),
@@ -822,6 +837,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
titleKey ?? 'ui.userfront.login.verification.title'; titleKey ?? 'ui.userfront.login.verification.title';
_verificationPageTitleKey = _verificationPageTitleKey =
pageTitleKey ?? 'ui.userfront.login.verification.page_title'; pageTitleKey ?? 'ui.userfront.login.verification.page_title';
_verificationActionLabelKey =
actionLabelKey ?? 'ui.userfront.login.verification.action_label';
_onVerificationAction = onAction; _onVerificationAction = onAction;
}); });
_verificationRedirectTimer?.cancel(); _verificationRedirectTimer?.cancel();
@@ -847,6 +864,15 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
webWindow.close(); webWindow.close();
} }
void _moveToSigninOrCloseVerificationWindow() {
if (webWindow.hasOpener()) {
webWindow.close();
return;
}
AuthTokenStore.skipNextCookieSessionCheck();
context.go(buildLocalizedSigninPath(Uri.base));
}
void _handleVerificationResultPrimaryAction() { void _handleVerificationResultPrimaryAction() {
if (_onVerificationAction != null) { if (_onVerificationAction != null) {
_runVerificationExitAction(); _runVerificationExitAction();
@@ -875,7 +901,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_markVerificationApproved( _markVerificationApproved(
'msg.userfront.login.verification.approved_remote', 'msg.userfront.login.verification.approved_remote',
titleKey: 'ui.userfront.login.verification.title_remote', titleKey: 'ui.userfront.login.verification.title_remote',
onAction: _closeVerificationWindowIfPossible, actionLabelKey: 'ui.userfront.login.verification.action_label_remote',
onAction: _moveToSigninOrCloseVerificationWindow,
); );
} }
@@ -890,13 +917,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
); );
final verificationTitle = tr(_verificationTitleKey); final verificationTitle = tr(_verificationTitleKey);
final closeHint = tr('msg.userfront.login.verification.close_hint'); final closeHint = tr('msg.userfront.login.verification.close_hint');
final showCloseHint = _onVerificationAction != null || _verificationOnly; final showCloseHint =
final actionLabelKey = showCloseHint _verificationActionLabelKey ==
? 'ui.userfront.login.verification.action_label_close' 'ui.userfront.login.verification.action_label_close';
: 'ui.userfront.login.verification.action_label';
final actionIcon = showCloseHint
? Icons.close_rounded
: Icons.arrow_forward_rounded;
return SafeArea( return SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
@@ -1001,11 +1024,13 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
), ),
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: FilledButton.icon( child: FilledButton(
onPressed: onPressed:
_handleVerificationResultPrimaryAction, _handleVerificationResultPrimaryAction,
icon: Icon(actionIcon), child: Text(
label: Text(tr(actionLabelKey)), tr(_verificationActionLabelKey),
textAlign: TextAlign.center,
),
), ),
), ),
), ),
@@ -1027,7 +1052,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
), ),
), ),
const SizedBox(height: 18), const SizedBox(height: 18),
Wrap( const Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
@@ -1116,6 +1141,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
} }
if (jwt is String && jwt.isNotEmpty) { if (jwt is String && jwt.isNotEmpty) {
if (_verificationOnly) {
_markRemoteVerificationApproved();
return;
}
if (hasLocalSession) { if (hasLocalSession) {
_markVerificationApproved( _markVerificationApproved(
'msg.userfront.login.verification.approved_local', 'msg.userfront.login.verification.approved_local',
@@ -2367,7 +2396,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Wrap( const Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,

View File

@@ -342,6 +342,8 @@ const Map<String, String> koStrings = {
"msg.common.requesting": "요청 중...", "msg.common.requesting": "요청 중...",
"msg.common.saving": "저장 중...", "msg.common.saving": "저장 중...",
"msg.common.unknown_error": "알 수 없는 오류", "msg.common.unknown_error": "알 수 없는 오류",
"msg.dev.audit.access_denied": "감사 로그는 개발자 권한이 있어야 볼 수 있습니다.",
"msg.dev.audit.access_denied_detail": "개발자 권한 신청 페이지에서 신청을 등록한 뒤 승인을 받아주세요.",
"msg.dev.audit.empty": "조회된 감사 로그가 없습니다.", "msg.dev.audit.empty": "조회된 감사 로그가 없습니다.",
"msg.dev.audit.forbidden": "감사 로그를 조회할 권한이 없습니다. 관리자에게 권한을 요청해주세요.", "msg.dev.audit.forbidden": "감사 로그를 조회할 권한이 없습니다. 관리자에게 권한을 요청해주세요.",
"msg.dev.audit.load_error": "감사 로그 조회 실패: {{error}}", "msg.dev.audit.load_error": "감사 로그 조회 실패: {{error}}",
@@ -730,8 +732,7 @@ const Map<String, String> koStrings = {
"msg.userfront.login.verification.approved": "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다.", "msg.userfront.login.verification.approved": "승인되었습니다. 로그인은 요청하신 창에서 완료됩니다.",
"msg.userfront.login.verification.approved_local": "msg.userfront.login.verification.approved_local":
"승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다", "승인 되었습니다. 이 기기는 로그인되어 있는 상태입니다. 원격 창도 로그인이 될 예정입니다",
"msg.userfront.login.verification.approved_remote": "msg.userfront.login.verification.approved_remote": "요청하신 로그인이 완료되었습니다",
"승인되었습니다.\n로그인 요청하신 화면으로 돌아가주세요.",
"msg.userfront.login.verification.close_hint": "이 창은 이제 닫으셔도 됩니다.", "msg.userfront.login.verification.close_hint": "이 창은 이제 닫으셔도 됩니다.",
"msg.userfront.login.verification.pending_remote": "msg.userfront.login.verification.pending_remote":
"승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요.", "승인 요청을 확인하고 있습니다. 잠시만 기다려 주세요.",
@@ -2199,6 +2200,7 @@ const Map<String, String> koStrings = {
"ui.userfront.login.unregistered.title": "미등록 회원", "ui.userfront.login.unregistered.title": "미등록 회원",
"ui.userfront.login.verification.action_label": "확인", "ui.userfront.login.verification.action_label": "확인",
"ui.userfront.login.verification.action_label_close": "창 닫기", "ui.userfront.login.verification.action_label_close": "창 닫기",
"ui.userfront.login.verification.action_label_remote": "로그인 창으로 이동하기",
"ui.userfront.login.verification.page_title": "Baron SW 포탈", "ui.userfront.login.verification.page_title": "Baron SW 포탈",
"ui.userfront.login.verification.title": "승인 완료", "ui.userfront.login.verification.title": "승인 완료",
"ui.userfront.login.verification.title_pending": "로그인 승인 확인 중", "ui.userfront.login.verification.title_pending": "로그인 승인 확인 중",
@@ -2693,6 +2695,10 @@ const Map<String, String> enStrings = {
"msg.common.requesting": "Requesting...", "msg.common.requesting": "Requesting...",
"msg.common.saving": "Saving...", "msg.common.saving": "Saving...",
"msg.common.unknown_error": "unknown error", "msg.common.unknown_error": "unknown error",
"msg.dev.audit.access_denied":
"Audit logs are available only to users with developer access.",
"msg.dev.audit.access_denied_detail":
"Submit a request on the developer access page and wait for approval.",
"msg.dev.audit.empty": "No audit logs found.", "msg.dev.audit.empty": "No audit logs found.",
"msg.dev.audit.forbidden": "msg.dev.audit.forbidden":
"You do not have permission to view audit logs. Please request access from an administrator.", "You do not have permission to view audit logs. Please request access from an administrator.",
@@ -3156,7 +3162,7 @@ const Map<String, String> enStrings = {
"msg.userfront.login.verification.approved_local": "msg.userfront.login.verification.approved_local":
"Approved. This device is already signed in, and the remote window will be signed in shortly.", "Approved. This device is already signed in, and the remote window will be signed in shortly.",
"msg.userfront.login.verification.approved_remote": "msg.userfront.login.verification.approved_remote":
"Approved.\nPlease return to the screen where you requested sign-in.", "Your requested sign-in is complete.",
"msg.userfront.login.verification.close_hint": "msg.userfront.login.verification.close_hint":
"You can close this window now.", "You can close this window now.",
"msg.userfront.login.verification.pending_remote": "msg.userfront.login.verification.pending_remote":
@@ -4704,6 +4710,7 @@ const Map<String, String> enStrings = {
"ui.userfront.login.unregistered.title": "Account not found", "ui.userfront.login.unregistered.title": "Account not found",
"ui.userfront.login.verification.action_label": "Done", "ui.userfront.login.verification.action_label": "Done",
"ui.userfront.login.verification.action_label_close": "Close Window", "ui.userfront.login.verification.action_label_close": "Close Window",
"ui.userfront.login.verification.action_label_remote": "Go to sign-in window",
"ui.userfront.login.verification.page_title": "Baron SW Portal", "ui.userfront.login.verification.page_title": "Baron SW Portal",
"ui.userfront.login.verification.title": "Approval complete", "ui.userfront.login.verification.title": "Approval complete",
"ui.userfront.login.verification.title_pending": "Checking approval", "ui.userfront.login.verification.title_pending": "Checking approval",

View File

@@ -45,10 +45,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.1" version: "1.4.0"
cli_config: cli_config:
dependency: transitive dependency: transitive
description: description:
@@ -328,18 +328,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.19" version: "0.12.17"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.0" version: "0.11.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -661,26 +661,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test name: test
sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.30.0" version: "1.26.3"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" version: "0.7.7"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.16" version: "0.6.12"
toml: toml:
dependency: "direct main" dependency: "direct main"
description: description: