1
0
forked from baron/baron-sso

dev 병합 code-check 오류 수정

This commit is contained in:
2026-04-28 13:23:40 +09:00
parent 6be0914b65
commit eae3e0bd2a
13 changed files with 349 additions and 67 deletions

View File

@@ -181,13 +181,8 @@ PLAYWRIGHT_CHROMIUM_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/chromium-1208/INSTAL
PLAYWRIGHT_FIREFOX_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/firefox-1509/INSTALLATION_COMPLETE
PLAYWRIGHT_WEBKIT_COMPLETE := $(PLAYWRIGHT_BROWSERS_PATH)/webkit-2248/INSTALLATION_COMPLETE
ifeq ($(CI),)
PLAYWRIGHT_INSTALL_ALL := sh -c 'if [ -f "$(PLAYWRIGHT_CHROMIUM_COMPLETE)" ] && [ -f "$(PLAYWRIGHT_FIREFOX_COMPLETE)" ] && [ -f "$(PLAYWRIGHT_WEBKIT_COMPLETE)" ]; then echo "Playwright browsers already installed"; else npx playwright install; fi'
PLAYWRIGHT_INSTALL_CHROMIUM := sh -c 'if [ -f "$(PLAYWRIGHT_CHROMIUM_COMPLETE)" ]; then echo "Playwright chromium already installed"; else npx playwright install chromium; fi'
else
PLAYWRIGHT_INSTALL_ALL := npx playwright install --with-deps
PLAYWRIGHT_INSTALL_CHROMIUM := npx playwright install --with-deps chromium
endif
.PHONY: code-check code-check-lint code-check-test-jobs code-check-i18n code-check-i18n-values code-check-go-lint code-check-sync-userfront-locales code-check-userfront-install code-check-userfront-lint code-check-front-lint code-check-backend-tests code-check-userfront-tests code-check-adminfront-tests code-check-devfront-tests code-check-userfront-e2e-tests
@@ -254,12 +249,12 @@ code-check-userfront-lint:
code-check-front-lint:
@echo "==> adminfront biome lint/format check"
rm -rf adminfront/playwright-report adminfront/test-results
cd adminfront && npm ci
cd adminfront && npm ci --ignore-scripts
cd adminfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
cd adminfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
@echo "==> devfront biome lint/format check"
rm -rf devfront/playwright-report devfront/test-results
cd devfront && npm ci
cd devfront && npm ci --ignore-scripts
cd devfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
cd devfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
@@ -298,7 +293,7 @@ code-check-devfront-tests:
@mkdir -p reports/devfront
@rm -rf reports/devfront/playwright-report reports/devfront/test-results
@status=0; \
(cd devfront && npm ci) || status=$$?; \
(cd devfront && npm ci --ignore-scripts) || status=$$?; \
if [ $$status -eq 0 ]; then \
(cd devfront && $(PLAYWRIGHT_INSTALL_ALL)) || status=$$?; \
fi; \

View File

@@ -1,8 +1,10 @@
package handler
import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/service"
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -13,6 +15,121 @@ import (
"github.com/stretchr/testify/mock"
)
// --- Mocks ---
type MockKratosAdminServiceForConsent struct {
mock.Mock
}
func (m *MockKratosAdminServiceForConsent) FindIdentityIDByIdentifier(ctx context.Context, identifier string) (string, error) {
args := m.Called(ctx, identifier)
return args.String(0), args.Error(1)
}
func (m *MockKratosAdminServiceForConsent) GetIdentity(ctx context.Context, id string) (*service.KratosIdentity, error) {
args := m.Called(ctx, id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*service.KratosIdentity), args.Error(1)
}
func (m *MockKratosAdminServiceForConsent) ListIdentities(ctx context.Context) ([]service.KratosIdentity, error) {
return nil, nil
}
func (m *MockKratosAdminServiceForConsent) UpdateIdentity(ctx context.Context, identityID string, traits map[string]interface{}, state string) (*service.KratosIdentity, error) {
return nil, nil
}
func (m *MockKratosAdminServiceForConsent) CreateIdentity(ctx context.Context, traits map[string]interface{}) (*service.KratosIdentity, error) {
return nil, nil
}
func (m *MockKratosAdminServiceForConsent) DeleteIdentity(ctx context.Context, identityID string) error {
return nil
}
func (m *MockKratosAdminServiceForConsent) UpdateIdentityPassword(ctx context.Context, identityID, newPassword string) error {
return nil
}
func (m *MockKratosAdminServiceForConsent) ListIdentitySessions(ctx context.Context, identityID string) ([]service.KratosSession, error) {
return nil, nil
}
func (m *MockKratosAdminServiceForConsent) GetSession(ctx context.Context, sessionID string) (*service.KratosSession, error) {
return nil, nil
}
func (m *MockKratosAdminServiceForConsent) DeleteSession(ctx context.Context, sessionID string) error {
return nil
}
func (m *MockKratosAdminServiceForConsent) CreateUser(ctx context.Context, user *domain.BrokerUser, password string) (string, error) {
return "", nil
}
type MockTenantServiceForConsent struct {
mock.Mock
}
func (m *MockTenantServiceForConsent) GetTenant(ctx context.Context, id string) (*domain.Tenant, error) {
args := m.Called(ctx, id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.Tenant), args.Error(1)
}
func (m *MockTenantServiceForConsent) GetTenantBySlug(ctx context.Context, slug string) (*domain.Tenant, error) {
return nil, nil
}
func (m *MockTenantServiceForConsent) GetTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) {
return nil, nil
}
func (m *MockTenantServiceForConsent) ListTenants(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
return nil, 0, nil
}
func (m *MockTenantServiceForConsent) RegisterTenant(ctx context.Context, name, slug, tenantType, description string, domains []string, parentID *string, creatorID string) (*domain.Tenant, error) {
return nil, nil
}
func (m *MockTenantServiceForConsent) RequestRegistration(ctx context.Context, name, slug, description string, domainName string, adminEmail string) (*domain.Tenant, error) {
return nil, nil
}
func (m *MockTenantServiceForConsent) ApproveTenant(ctx context.Context, id string) error {
return nil
}
func (m *MockTenantServiceForConsent) ListManageableTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
args := m.Called(ctx, userID)
return args.Get(0).([]domain.Tenant), args.Error(1)
}
func (m *MockTenantServiceForConsent) ListJoinedTenants(ctx context.Context, userID string) ([]domain.Tenant, error) {
args := m.Called(ctx, userID)
return args.Get(0).([]domain.Tenant), args.Error(1)
}
func (m *MockTenantServiceForConsent) IsDomainAllowed(ctx context.Context, domainName string) (bool, error) {
return false, nil
}
func (m *MockTenantServiceForConsent) ProvisionTenantByDomain(ctx context.Context, domainName string) (*domain.Tenant, error) {
return nil, nil
}
func (m *MockTenantServiceForConsent) SetKetoService(keto service.KetoService) {}
func (m *MockTenantServiceForConsent) DeleteTenantsBulk(ctx context.Context, ids []string) error {
return nil
}
// --- Test Helpers ---
func newConsentTestApp(h *AuthHandler) *fiber.App {
@@ -100,11 +217,36 @@ func TestGetConsentRequest_AddsMandatoryTenantScope(t *testing.T) {
http.DefaultClient = client
defer func() { http.DefaultClient = origDefault }()
mockTenantSvc := &MockTenantServiceForConsent{}
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
// Mock profile resolution to allow tenant access
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
ID: "user-123",
Traits: map[string]interface{}{
"email": "user@example.com",
},
}, nil)
mockTenantSvc.On("GetTenant", mock.Anything, "tenant-allow").Return(&domain.Tenant{
ID: "tenant-allow",
Slug: "tenant-allow",
Name: "Allowed Tenant",
}, nil)
// Mock hydration calls
mockTenantSvc.On("ListJoinedTenants", mock.Anything, mock.Anything).Return([]domain.Tenant{
{ID: "tenant-allow", Slug: "tenant-allow", Name: "Allowed Tenant"},
}, nil)
mockTenantSvc.On("ListManageableTenants", mock.Anything, mock.Anything).Return([]domain.Tenant{}, nil)
h := &AuthHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: client,
},
TenantService: mockTenantSvc,
KratosAdmin: mockKratosAdmin,
}
app := newConsentTestApp(h)
@@ -163,16 +305,17 @@ func TestGetConsentRequest_Skip_AutoAccept(t *testing.T) {
defer func() { http.DefaultClient = origDefault }()
consentRepo := &mockConsentRepo{}
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
h := &AuthHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: client,
},
KratosAdmin: new(MockKratosAdminService), // Reusing MockKratosAdminService if defined or use MockKratosAdminServiceShared
KratosAdmin: mockKratosAdmin,
ConsentRepo: consentRepo,
}
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
ID: "user-123",
Traits: map[string]interface{}{
"email": "user@test.com",
@@ -199,7 +342,8 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
"requested_scope": []string{"openid", "profile"},
"subject": "user-123",
"client": map[string]interface{}{
"client_id": "client-app",
"client_id": "client-app",
"client_name": "Test App",
},
}), nil
}
@@ -226,17 +370,18 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
auditRepo := &mockAuditRepo{}
consentRepo := &mockConsentRepo{}
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
h := &AuthHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: client,
},
KratosAdmin: new(MockKratosAdminService),
KratosAdmin: mockKratosAdmin,
AuditRepo: auditRepo,
ConsentRepo: consentRepo,
}
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
ID: "user-123",
Traits: map[string]interface{}{
"email": "user@test.com",
@@ -260,6 +405,8 @@ func TestAcceptConsentRequest_Normal(t *testing.T) {
}
func TestAcceptConsentRequest_EnforcesMandatoryTenantScope(t *testing.T) {
t.Setenv("APP_ENV", "dev")
var capturedGrantScopes []string
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
@@ -309,14 +456,16 @@ func TestAcceptConsentRequest_EnforcesMandatoryTenantScope(t *testing.T) {
http.DefaultClient = client
defer func() { http.DefaultClient = origDefault }()
mockKratosAdmin := &MockKratosAdminServiceForConsent{}
h := &AuthHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: client,
},
KratosAdmin: new(MockKratosAdminService),
KratosAdmin: mockKratosAdmin,
}
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
mockKratosAdmin.On("GetIdentity", mock.Anything, "user-123").Return(&service.KratosIdentity{
ID: "user-123",
Traits: map[string]interface{}{
"email": "user@test.com",

View File

@@ -184,6 +184,7 @@ func TestGetConsentRequest_DeniesTenantAccess(t *testing.T) {
app := fiber.New()
app.Get("/api/v1/auth/consent", h.GetConsentRequest)
t.Setenv("APP_ENV", "dev")
req := httptest.NewRequest(http.MethodGet, "/api/v1/auth/consent?consent_challenge=challenge-tenant", nil)
req.Header.Set("X-Mock-Role", "user")
req.Header.Set("X-Tenant-ID", "tenant-a")

View File

@@ -13,47 +13,47 @@ import (
// Ory 계열(kratos/hydra) 공급자 문자열을 정규화하기 위한 매핑.
var providerAliases = map[string]string{
"ory": "ory",
"hydra": "ory",
"kratos": "ory",
"ory-kratos": "ory",
"ory_hydra": "ory",
"ory_kratos": "ory",
"ory": "ory",
"hydra": "ory",
"kratos": "ory",
"ory-kratos": "ory",
"ory_hydra": "ory",
"ory_kratos": "ory",
}
// getEnv는 환경 변수를 읽거나 대체 값을 반환하는 헬퍼 함수입니다.
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
// InitializeProvider는 환경 설정을 기반으로 IDP 공급자를 생성하고 반환합니다.
// 이것은 IdentityProvider 인터페이스의 팩토리 역할을 합니다.
func InitializeProvider() (domain.IdentityProvider, error) {
rawProviders := getEnv("IDP_PROVIDER", "ory")
providers := strings.Split(rawProviders, ",")
slog.Info("Initializing IDP chain", "providers", rawProviders)
rawProviders := getEnv("IDP_PROVIDER", "ory")
providers := strings.Split(rawProviders, ",")
slog.Info("Initializing IDP chain", "providers", rawProviders)
var initialized []domain.IdentityProvider
for _, p := range providers {
providerName := strings.TrimSpace(strings.ToLower(p))
if canonical, ok := providerAliases[providerName]; ok {
providerName = canonical
}
var initialized []domain.IdentityProvider
for _, p := range providers {
providerName := strings.TrimSpace(strings.ToLower(p))
if canonical, ok := providerAliases[providerName]; ok {
providerName = canonical
}
switch providerName {
case "ory":
// Kratos/Hydra 주 공급자
oryProvider := service.NewOryProvider()
initialized = append(initialized, oryProvider)
switch providerName {
case "ory":
// Kratos/Hydra 주 공급자
oryProvider := service.NewOryProvider()
initialized = append(initialized, oryProvider)
default:
// 알 수 없는 공급자는 건너뛰고 다음 후보를 시도
slog.Warn("Skipping unsupported IDP provider entry", "provider", providerName)
}
}
default:
// 알 수 없는 공급자는 건너뛰고 다음 후보를 시도
slog.Warn("Skipping unsupported IDP provider entry", "provider", providerName)
}
}
if len(initialized) == 0 {
return nil, fmt.Errorf("no valid IDP_PROVIDER entries configured from: %s", rawProviders)
}

View File

@@ -63,7 +63,7 @@ func TestMain(m *testing.M) {
}
// Auto-migrate
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{})
err = db.AutoMigrate(&domain.Tenant{}, &domain.TenantDomain{}, &domain.User{}, &domain.ClientConsent{})
if err != nil {
log.Fatalf("failed to migrate database: %s", err)
}

View File

@@ -174,10 +174,7 @@ function ClientGeneralPage() {
{
id: "2",
name: "tenant",
description: t(
"msg.dev.clients.scopes.tenant",
"소속 테넌트 정보 접근",
),
description: t("msg.dev.clients.scopes.tenant", "소속 테넌트 정보 접근"),
mandatory: false,
},
{
@@ -347,12 +344,15 @@ function ClientGeneralPage() {
return {
...scope,
description: scope.description || tenantScopeDescription,
mandatory: restricted ? true : false,
mandatory: restricted,
locked: restricted,
};
});
if (restricted && !normalized.some((scope) => scope.name.trim() === "tenant")) {
if (
restricted &&
!normalized.some((scope) => scope.name.trim() === "tenant")
) {
normalized.push(buildTenantScope(`tenant-${Date.now()}`));
}
@@ -526,7 +526,8 @@ function ClientGeneralPage() {
const hasValidationErrors = validationErrors.length > 0;
const normalizedTenantSearch = tenantSearch.trim().toLowerCase();
const tenantOptions: Array<TenantSummary | MyTenantSummary> = tenantData ?? [];
const tenantOptions: Array<TenantSummary | MyTenantSummary> =
tenantData ?? [];
const filteredTenants = tenantOptions.filter((tenant) => {
if (!normalizedTenantSearch) {
return true;
@@ -1375,10 +1376,7 @@ function ClientGeneralPage() {
</span>
<button
type="button"
aria-label={t(
"ui.common.delete",
"삭제",
)}
aria-label={t("ui.common.delete", "삭제")}
onClick={() => toggleAllowedTenant(tenant.id)}
className="text-muted-foreground transition hover:text-destructive"
>
@@ -1403,10 +1401,7 @@ function ClientGeneralPage() {
<span className="max-w-44 truncate">{tenantId}</span>
<button
type="button"
aria-label={t(
"ui.common.delete",
"삭제",
)}
aria-label={t("ui.common.delete", "삭제")}
onClick={() => toggleAllowedTenant(tenantId)}
className="text-muted-foreground transition hover:text-destructive"
>

View File

@@ -650,6 +650,22 @@ title_generic = "An error occurred."
title_with_code = "Error: {{code}}"
type = "Error type: {{type}}"
[msg.userfront.error.tenant]
account = "Account"
account_unknown = "Unknown"
affiliated_tenants = "All affiliated tenants"
allowed_box_title = "Allowed tenants"
allowed_tenants = "Allowed tenants"
detail = "The currently signed-in account cannot access this application."
load_failed = "We could not confirm the account details. Please try again."
loading = "Loading the current account details."
lookup_fallback = "Some fields could not be verified because the access context was incomplete."
page_title = "Access to this application is restricted"
primary_tenant = "Primary affiliated tenant"
tenant = "Tenant"
tenant_unknown = "Unknown"
title = "Access restriction details"
[msg.userfront.error.ory]
"$normalizedCode" = "{{error}}"
access_denied = "The user denied the consent request."
@@ -2239,6 +2255,7 @@ windows = "Desktop(Windows)"
[ui.userfront.error]
go_home = "Go Home"
go_login = "Go Login"
switch_account = "Sign in with another account"
[ui.userfront.forgot]
heading = "Forgot your password?"

View File

@@ -208,6 +208,22 @@ title_generic = "오류가 발생했습니다"
title_with_code = "오류: {{code}}"
type = "오류 종류: {{type}}"
[msg.userfront.error.tenant]
account = "계정"
account_unknown = "알 수 없음"
affiliated_tenants = "전체 소속 테넌트"
allowed_box_title = "접속 가능 테넌트"
allowed_tenants = "접속 가능 테넌트"
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
loading = "현재 계정 정보를 불러오는 중입니다."
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
page_title = "애플리케이션 접근이 제한되었습니다"
primary_tenant = "대표 소속 테넌트"
tenant = "소속 테넌트"
tenant_unknown = "알 수 없음"
title = "접근 제한 정보"
[msg.userfront.forgot]
description = "계정과 연결된 이메일 주소 또는 휴대폰 번호를 입력하시면, 비밀번호를 재설정할 수 있는 링크를 보내드립니다."
dry_send = "drySend 모드: 실제 이메일/SMS는 발송되지 않습니다."
@@ -456,6 +472,7 @@ windows = "Desktop(Windows)"
[ui.userfront.error]
go_home = "홈으로 이동"
go_login = "로그인으로 이동"
switch_account = "다른 계정으로 로그인"
[ui.userfront.forgot]
heading = "비밀번호를 잊으셨나요?"
@@ -1088,6 +1105,22 @@ title_generic = "오류가 발생했습니다"
title_with_code = "오류: {{code}}"
type = "오류 종류: {{type}}"
[msg.userfront.error.tenant]
account = "계정"
account_unknown = "알 수 없음"
affiliated_tenants = "전체 소속 테넌트"
allowed_box_title = "접속 가능 테넌트"
allowed_tenants = "접속 가능 테넌트"
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
loading = "현재 계정 정보를 불러오는 중입니다."
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
page_title = "애플리케이션 접근이 제한되었습니다"
primary_tenant = "대표 소속 테넌트"
tenant = "소속 테넌트"
tenant_unknown = "알 수 없음"
title = "접근 제한 정보"
[msg.userfront.error.ory]
"$normalizedCode" = "{{error}}"
access_denied = "사용자가 동의를 거부했습니다."
@@ -2638,6 +2671,7 @@ windows = "Desktop(Windows)"
[ui.userfront.error]
go_home = "홈으로 이동"
go_login = "로그인으로 이동"
switch_account = "다른 계정으로 로그인"
[ui.userfront.forgot]
heading = "비밀번호를 잊으셨나요?"

View File

@@ -83,6 +83,22 @@ title_generic = ""
title_with_code = ""
type = ""
[msg.userfront.error.tenant]
account = ""
account_unknown = ""
affiliated_tenants = ""
allowed_box_title = ""
allowed_tenants = ""
detail = ""
load_failed = ""
loading = ""
lookup_fallback = ""
page_title = ""
primary_tenant = ""
tenant = ""
tenant_unknown = ""
title = ""
[msg.userfront.forgot]
description = ""
dry_send = ""
@@ -331,6 +347,7 @@ windows = ""
[ui.userfront.error]
go_home = ""
go_login = ""
switch_account = ""
[ui.userfront.forgot]
heading = ""
@@ -963,6 +980,22 @@ title_generic = ""
title_with_code = ""
type = ""
[msg.userfront.error.tenant]
account = ""
account_unknown = ""
affiliated_tenants = ""
allowed_box_title = ""
allowed_tenants = ""
detail = ""
load_failed = ""
loading = ""
lookup_fallback = ""
page_title = ""
primary_tenant = ""
tenant = ""
tenant_unknown = ""
title = ""
[msg.userfront.error.ory]
"$normalizedCode" = ""
access_denied = ""
@@ -2513,6 +2546,7 @@ windows = ""
[ui.userfront.error]
go_home = ""
go_login = ""
switch_account = ""
[ui.userfront.forgot]
heading = ""

View File

@@ -5,9 +5,14 @@ job_name="${1:-adminfront-tests}"
mkdir -p reports
if [ -n "${CI:-}" ]; then
playwright_install_cmd=(npx playwright install --with-deps)
playwright_install_desc="npx playwright install --with-deps"
PLAYWRIGHT_BROWSERS_PATH="${HOME}/.cache/ms-playwright"
PLAYWRIGHT_CHROMIUM_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/chromium-1208/INSTALLATION_COMPLETE"
PLAYWRIGHT_FIREFOX_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/firefox-1509/INSTALLATION_COMPLETE"
PLAYWRIGHT_WEBKIT_COMPLETE="${PLAYWRIGHT_BROWSERS_PATH}/webkit-2248/INSTALLATION_COMPLETE"
if [ -f "$PLAYWRIGHT_CHROMIUM_COMPLETE" ] && [ -f "$PLAYWRIGHT_FIREFOX_COMPLETE" ] && [ -f "$PLAYWRIGHT_WEBKIT_COMPLETE" ]; then
playwright_install_cmd=(sh -c 'echo "Playwright browsers already installed"')
playwright_install_desc="Playwright browsers already installed"
else
playwright_install_cmd=(npx playwright install)
playwright_install_desc="npx playwright install"
@@ -16,7 +21,7 @@ fi
set +e
(
cd adminfront
npm ci
npm ci --ignore-scripts
) 2>&1 | tee reports/adminfront-install.log
install_exit_code=${PIPESTATUS[0]}
set -e
@@ -31,7 +36,7 @@ if [ "$install_exit_code" -ne 0 ]; then
echo "- Exit Code: \`$install_exit_code\`"
echo
echo "## Command"
echo "\`cd adminfront && npm ci\`"
echo "\`cd adminfront && npm ci --ignore-scripts\`"
echo
echo "## Install Log Tail (last 200 lines)"
echo '```text'
@@ -70,7 +75,7 @@ if [ "$provision_exit_code" -ne 0 ]; then
fi
set +e
port="$(node -e "const net=require('node:net'); const s=net.createServer(); s.listen(0,'127.0.0.1',()=>{console.log(s.address().port); s.close();});")"
port="${PORT:-5173}"
echo "==> adminfront using PORT=$port"
(
cd adminfront
@@ -89,7 +94,7 @@ if [ "$test_exit_code" -ne 0 ]; then
echo
echo "## Commands"
echo "1. \`cd adminfront\`"
echo "2. \`npm ci\`"
echo "2. \`npm ci --ignore-scripts\`"
echo "3. \`${playwright_install_desc}\`"
echo "4. \`npm test\`"
echo

View File

@@ -140,9 +140,15 @@ type = "Error type: {type}"
[msg.userfront.error.tenant]
account = "Account"
account_unknown = "Unknown"
affiliated_tenants = "All affiliated tenants"
allowed_box_title = "Allowed tenants"
allowed_tenants = "Allowed tenants"
detail = "The currently signed-in account cannot access this application."
load_failed = "We could not confirm the account details. Please try again."
loading = "Loading the current account details."
lookup_fallback = "Some fields could not be verified because the access context was incomplete."
page_title = "Access to this application is restricted"
primary_tenant = "Primary affiliated tenant"
tenant = "Tenant"
tenant_unknown = "Unknown"
title = "Access restriction details"

View File

@@ -81,9 +81,15 @@ type = "오류 종류: {type}"
[msg.userfront.error.tenant]
account = "계정"
account_unknown = "알 수 없음"
affiliated_tenants = "전체 소속 테넌트"
allowed_box_title = "접속 가능 테넌트"
allowed_tenants = "접속 가능 테넌트"
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
loading = "현재 계정 정보를 불러오는 중입니다."
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
page_title = "애플리케이션 접근이 제한되었습니다"
primary_tenant = "대표 소속 테넌트"
tenant = "소속 테넌트"
tenant_unknown = "알 수 없음"
title = "접근 제한 정보"
@@ -355,6 +361,22 @@ title_generic = "오류가 발생했습니다"
title_with_code = "오류: {code}"
type = "오류 종류: {type}"
[msg.userfront.error.tenant]
account = "계정"
account_unknown = "알 수 없음"
affiliated_tenants = "전체 소속 테넌트"
allowed_box_title = "접속 가능 테넌트"
allowed_tenants = "접속 가능 테넌트"
detail = "현재 로그인된 계정은 이 애플리케이션에 접근할 수 없습니다."
load_failed = "계정 정보를 확인하지 못했습니다. 다시 시도해 주세요."
loading = "현재 계정 정보를 불러오는 중입니다."
lookup_fallback = "표시 정보가 충분하지 않아 일부 항목은 확인되지 않을 수 있습니다."
page_title = "애플리케이션 접근이 제한되었습니다"
primary_tenant = "대표 소속 테넌트"
tenant = "소속 테넌트"
tenant_unknown = "알 수 없음"
title = "접근 제한 정보"
[msg.userfront.error.ory]
"$normalizedCode" = "{error}"
access_denied = "사용자가 동의를 거부했습니다."
@@ -722,6 +744,7 @@ windows = "Desktop(Windows)"
[ui.userfront.error]
go_home = "홈으로 이동"
go_login = "로그인으로 이동"
switch_account = "다른 계정으로 로그인"
[ui.userfront.forgot]
heading = "비밀번호를 잊으셨나요?"

View File

@@ -53,9 +53,15 @@ type = ""
[msg.userfront.error.tenant]
account = ""
account_unknown = ""
affiliated_tenants = ""
allowed_box_title = ""
allowed_tenants = ""
detail = ""
load_failed = ""
loading = ""
lookup_fallback = ""
page_title = ""
primary_tenant = ""
tenant = ""
tenant_unknown = ""
title = ""
@@ -327,6 +333,22 @@ title_generic = ""
title_with_code = ""
type = ""
[msg.userfront.error.tenant]
account = ""
account_unknown = ""
affiliated_tenants = ""
allowed_box_title = ""
allowed_tenants = ""
detail = ""
load_failed = ""
loading = ""
lookup_fallback = ""
page_title = ""
primary_tenant = ""
tenant = ""
tenant_unknown = ""
title = ""
[msg.userfront.error.ory]
"$normalizedCode" = ""
access_denied = ""
@@ -694,6 +716,7 @@ windows = ""
[ui.userfront.error]
go_home = ""
go_login = ""
switch_account = ""
[ui.userfront.forgot]
heading = ""