diff --git a/adminfront/src/features/user-groups/routes/TenantUserGroupsTab.tsx b/adminfront/src/features/user-groups/routes/TenantUserGroupsTab.tsx
index bf715d01..a64b0c38 100644
--- a/adminfront/src/features/user-groups/routes/TenantUserGroupsTab.tsx
+++ b/adminfront/src/features/user-groups/routes/TenantUserGroupsTab.tsx
@@ -127,8 +127,9 @@ const SidebarNode: React.FC<{
return (
-
{node.recursiveMemberCount}
-
+
{isExpanded && hasChildren && (
@@ -233,7 +234,7 @@ const MemberTable: React.FC<{
{t("ui.admin.users.table.role", "역할")}
-
+
@@ -307,22 +308,22 @@ function TenantUserGroupsTab() {
return buildTenantFullTree(allItems, tenantId);
}, [allTenantsData, tenantId]);
- // Find selected node in the built tree
- const findNode = (nodes: TenantNode[], id: string): TenantNode | null => {
- if (!currentBase) return null;
- if (currentBase.id === id) return currentBase;
-
- for (const node of nodes) {
- if (node.id === id) return node;
- if (node.children.length > 0) {
- const found = findNode(node.children, id);
- if (found) return found;
- }
- }
- return null;
- };
-
const selectedNode = useMemo(() => {
+ // Find selected node in the built tree
+ const findNode = (nodes: TenantNode[], id: string): TenantNode | null => {
+ if (!currentBase) return null;
+ if (currentBase.id === id) return currentBase;
+
+ for (const node of nodes) {
+ if (node.id === id) return node;
+ if (node.children.length > 0) {
+ const found = findNode(node.children, id);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+
if (!currentBase) return null;
return findNode(currentBase.children, selectedNodeId) || currentBase;
}, [currentBase, selectedNodeId]);
@@ -707,9 +708,10 @@ const UserAddDialog: React.FC<{
toast.success(t("msg.info.saved_success", "사용자가 배정되었습니다."));
onOpenChange(false);
resetFields();
- } catch (err: any) {
+ } catch (err) {
+ const error = err as AxiosError<{ error?: string }>;
toast.error(
- err.response?.data?.error ||
+ error.response?.data?.error ||
t("msg.admin.users.detail.update_error", "배정 실패"),
);
} finally {
diff --git a/docker/compose.ory.yaml b/docker/compose.ory.yaml
index 5f18ca75..aae3dca6 100644
--- a/docker/compose.ory.yaml
+++ b/docker/compose.ory.yaml
@@ -88,7 +88,6 @@ services:
- ory-net
- hydranet
- # [수정됨] Oathkeeper 서비스 추가 (Backend 연결 문제 해결)
oathkeeper:
image: oryd/oathkeeper:${OATHKEEPER_VERSION:-v0.40.6}
container_name: oathkeeper
@@ -104,18 +103,83 @@ services:
- oathkeeper_logs:/var/log/oathkeeper
networks:
- ory-net
- - baron_net # Backend가 통신하기 위해 필수
+ - baron_net
- public_net
ports:
- - "4455:4455" # Proxy
- - "4456:4456" # API (Backend 헬스체크용)
+ - "4455:4455"
+ - "4456:4456"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:4456/health/ready"]
interval: 5s
timeout: 5s
retries: 5
+
+ ory_stack_check:
+ image: alpine:latest
+ container_name: ory_stack_check
+ command: >
+ /bin/sh -c "
+ apk add --no-cache curl;
+ echo 'Wait for services...';
+ until curl -s http://kratos:4433/health/ready; do sleep 1; done;
+ until curl -s http://hydra:4444/health/ready; do sleep 1; done;
+ echo 'Ory Stack is fully operational!';"
+ depends_on:
+ - kratos
+ - hydra
+ networks:
+ - ory-net
+
+ init-rp:
+ image: oryd/hydra:${HYDRA_VERSION:-v25.4.0}
+ container_name: init-rp
+ entrypoint: ["/bin/sh"]
+ command:
+ - -ec
+ - |
+ echo "Creating/Updating OAuth2 Clients..."
+
+ hydra create oauth2-client \
+ --endpoint http://hydra:4445 \
+ --id adminfront \
+ --name "AdminFront" \
+ --grant-type authorization_code,refresh_token \
+ --response-type code \
+ --scope openid,offline_access,profile,email \
+ --token-endpoint-auth-method none \
+ --redirect-uri ${ADMINFRONT_CALLBACK_URLS:-http://localhost:5173/auth/callback,http://172.16.10.176:5173/auth/callback}
+
+ hydra create oauth2-client \
+ --endpoint http://hydra:4445 \
+ --id devfront \
+ --name "DevFront" \
+ --grant-type authorization_code,refresh_token \
+ --response-type code \
+ --scope openid,offline_access,profile,email \
+ --token-endpoint-auth-method none \
+ --redirect-uri ${DEVFRONT_CALLBACK_URLS:-http://localhost:5174/auth/callback,http://172.16.10.176:5174/auth/callback}
+
+ hydra create oauth2-client \
+ --endpoint http://hydra:4445 \
+ --id orgfront \
+ --name "OrgFront" \
+ --grant-type authorization_code,refresh_token \
+ --response-type code \
+ --scope openid,offline_access,profile,email \
+ --token-endpoint-auth-method none \
+ --redirect-uri ${ORGFRONT_CALLBACK_URLS:-http://localhost:5175/auth/callback,http://172.16.10.176:5175/auth/callback,https://baron-orgchart.hmac.kr/auth/callback}
+
+ echo "All RP clients initialized successfully."
+ depends_on:
+ ory_stack_check:
+ condition: service_completed_successfully
+ networks:
+ - ory-net
+ - hydranet
+
volumes:
ory_postgres_data:
+ oathkeeper_logs:
networks:
ory-net:
@@ -130,7 +194,6 @@ networks:
public_net:
external: true
name: public_net
- # [수정됨] Baron Net 추가 정의 (Oathkeeper 연결용)
baron_net:
external: true
name: baron_net