97b333dd2e
부팅 시 자동 시드: - 외부 REST API 연결 6종 (부서/사원/거래처/창고/계정과목/Wehago 사용자) - 매칭 배치 6개 + Wehago HMAC-SHA256 서명 자동 부착 (erpApiClient/erpPresetSeedService/erpBatchSeedService) - 동기화 대상 테이블/컬럼 보장 idempotent 마이그레이션 (erpTableMigration) 배치 기능 확장: - 조건부 매핑 (mapping_type='conditional') — when/then/default 규칙으로 값 변환 (예: enrlFg=J01→active) - 행 단위 제외 필터 (row_filter_config) — 특정 API 필드 값 가진 행 동기화 제외 (예: loginId=wace 통합 ERP 계정 제외) - save_mode/conflict_key 기반 UPSERT, data_array_path 응답 배열 추출 UI 정비: - 배치/플로우/메일/REST API 목록에 페이징 + FullHD 컴팩트 레이아웃 - 배치 편집 화면 한 화면 풀 활용 — TO 패널 가로 그리드, FROM 패널 등록 연결 한 줄 요약, 응답/JSON/파라미터 details 접힘 - ResponsiveDataView/AdminPageRenderer 쿼리 파라미터(?edit=N) 파싱 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
365 lines
13 KiB
TypeScript
365 lines
13 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { AuthType } from "@/lib/api/externalRestApiConnection";
|
|
|
|
interface AuthenticationConfigProps {
|
|
authType: AuthType;
|
|
authConfig: any;
|
|
onAuthTypeChange: (type: AuthType) => void;
|
|
onAuthConfigChange: (config: any) => void;
|
|
}
|
|
|
|
export function AuthenticationConfig({
|
|
authType,
|
|
authConfig = {},
|
|
onAuthTypeChange,
|
|
onAuthConfigChange,
|
|
}: AuthenticationConfigProps) {
|
|
// 인증 설정 변경
|
|
const updateAuthConfig = (field: string, value: string) => {
|
|
onAuthConfigChange({
|
|
...authConfig,
|
|
[field]: value,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* 인증 타입 선택 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="auth-type">인증 타입</Label>
|
|
<Select value={authType} onValueChange={(value) => onAuthTypeChange(value as AuthType)}>
|
|
<SelectTrigger id="auth-type">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="none">인증 없음</SelectItem>
|
|
<SelectItem value="api-key">API Key</SelectItem>
|
|
<SelectItem value="bearer">Bearer Token</SelectItem>
|
|
<SelectItem value="basic">Basic Auth</SelectItem>
|
|
<SelectItem value="oauth2">OAuth 2.0</SelectItem>
|
|
<SelectItem value="db-token">DB 토큰</SelectItem>
|
|
<SelectItem value="wehago">Wehago / Amaranth (아마란스)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 인증 타입별 설정 필드 */}
|
|
{authType === "api-key" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<h4 className="text-sm font-medium">API Key 설정</h4>
|
|
|
|
{/* 키 위치 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="key-location">키 위치</Label>
|
|
<Select
|
|
value={authConfig.keyLocation || "header"}
|
|
onValueChange={(value) => updateAuthConfig("keyLocation", value)}
|
|
>
|
|
<SelectTrigger id="key-location">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="header">Header</SelectItem>
|
|
<SelectItem value="query">Query Parameter</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 키 이름 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="key-name">키 이름</Label>
|
|
<Input
|
|
id="key-name"
|
|
type="text"
|
|
value={authConfig.keyName || ""}
|
|
onChange={(e) => updateAuthConfig("keyName", e.target.value)}
|
|
placeholder="예: X-API-Key"
|
|
/>
|
|
</div>
|
|
|
|
{/* 키 값 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="key-value">키 값</Label>
|
|
<Input
|
|
id="key-value"
|
|
type="password"
|
|
value={authConfig.keyValue || ""}
|
|
onChange={(e) => updateAuthConfig("keyValue", e.target.value)}
|
|
placeholder="API Key를 입력하세요"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "bearer" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<h4 className="text-sm font-medium">Bearer Token 설정</h4>
|
|
|
|
{/* 토큰 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="token">Token</Label>
|
|
<Input
|
|
id="token"
|
|
type="password"
|
|
value={authConfig.token || ""}
|
|
onChange={(e) => updateAuthConfig("token", e.target.value)}
|
|
placeholder="Bearer Token을 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
* Authorization 헤더에 "Bearer {token}" 형식으로 전송됩니다.
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "basic" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<h4 className="text-sm font-medium">Basic Auth 설정</h4>
|
|
|
|
{/* 사용자명 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="username">사용자명</Label>
|
|
<Input
|
|
id="username"
|
|
type="text"
|
|
value={authConfig.username || ""}
|
|
onChange={(e) => updateAuthConfig("username", e.target.value)}
|
|
placeholder="사용자명을 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
{/* 비밀번호 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="password">비밀번호</Label>
|
|
<Input
|
|
id="password"
|
|
type="password"
|
|
value={authConfig.password || ""}
|
|
onChange={(e) => updateAuthConfig("password", e.target.value)}
|
|
placeholder="비밀번호를 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">* Authorization 헤더에 Base64 인코딩된 인증 정보가 전송됩니다.</p>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "oauth2" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<h4 className="text-sm font-medium">OAuth 2.0 설정</h4>
|
|
|
|
{/* Client ID */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="client-id">Client ID</Label>
|
|
<Input
|
|
id="client-id"
|
|
type="text"
|
|
value={authConfig.clientId || ""}
|
|
onChange={(e) => updateAuthConfig("clientId", e.target.value)}
|
|
placeholder="Client ID를 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
{/* Client Secret */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="client-secret">Client Secret</Label>
|
|
<Input
|
|
id="client-secret"
|
|
type="password"
|
|
value={authConfig.clientSecret || ""}
|
|
onChange={(e) => updateAuthConfig("clientSecret", e.target.value)}
|
|
placeholder="Client Secret을 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
{/* Token URL */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="token-url">Token URL</Label>
|
|
<Input
|
|
id="token-url"
|
|
type="text"
|
|
value={authConfig.tokenUrl || ""}
|
|
onChange={(e) => updateAuthConfig("tokenUrl", e.target.value)}
|
|
placeholder="예: https://oauth.example.com/token"
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">* OAuth 2.0 Client Credentials Grant 방식을 사용합니다.</p>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "db-token" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<h4 className="text-sm font-medium">DB 기반 토큰 설정</h4>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-table-name">테이블명</Label>
|
|
<Input
|
|
id="db-table-name"
|
|
type="text"
|
|
value={authConfig.dbTableName || ""}
|
|
onChange={(e) => updateAuthConfig("dbTableName", e.target.value)}
|
|
placeholder="예: auth_tokens"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-value-column">값 컬럼명</Label>
|
|
<Input
|
|
id="db-value-column"
|
|
type="text"
|
|
value={authConfig.dbValueColumn || ""}
|
|
onChange={(e) =>
|
|
updateAuthConfig("dbValueColumn", e.target.value)
|
|
}
|
|
placeholder="예: access_token"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-where-column">조건 컬럼명</Label>
|
|
<Input
|
|
id="db-where-column"
|
|
type="text"
|
|
value={authConfig.dbWhereColumn || ""}
|
|
onChange={(e) =>
|
|
updateAuthConfig("dbWhereColumn", e.target.value)
|
|
}
|
|
placeholder="예: service_name"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-where-value">조건 값</Label>
|
|
<Input
|
|
id="db-where-value"
|
|
type="text"
|
|
value={authConfig.dbWhereValue || ""}
|
|
onChange={(e) =>
|
|
updateAuthConfig("dbWhereValue", e.target.value)
|
|
}
|
|
placeholder="예: kakao"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-header-name">헤더 이름 (선택)</Label>
|
|
<Input
|
|
id="db-header-name"
|
|
type="text"
|
|
value={authConfig.dbHeaderName || ""}
|
|
onChange={(e) =>
|
|
updateAuthConfig("dbHeaderName", e.target.value)
|
|
}
|
|
placeholder="기본값: Authorization"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="db-header-template">
|
|
헤더 템플릿 (선택, {{value}} 치환)
|
|
</Label>
|
|
<Input
|
|
id="db-header-template"
|
|
type="text"
|
|
value={authConfig.dbHeaderTemplate || ""}
|
|
onChange={(e) =>
|
|
updateAuthConfig("dbHeaderTemplate", e.target.value)
|
|
}
|
|
placeholder='기본값: "Bearer {{value}}"'
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
company_code는 현재 로그인한 사용자의 회사 코드로 자동 필터링됩니다.
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "wehago" && (
|
|
<div className="space-y-4 rounded-md border bg-muted p-4">
|
|
<div className="flex items-center justify-between">
|
|
<h4 className="text-sm font-medium">Wehago / Amaranth (아마란스) 설정</h4>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
onAuthConfigChange({
|
|
callerName: "API_gcmsAmaranth40578",
|
|
accessToken: "MN5KzKBWRAa92BPxDlRLl3GcsxeZXc",
|
|
hashKey: "22519103205540290721741689643674301018832465",
|
|
groupSeq: "gcmsAmaranth40578",
|
|
})
|
|
}
|
|
className="text-xs text-primary underline-offset-2 hover:underline"
|
|
>
|
|
RPS Amaranth 기본값 채우기
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="wehago-caller">callerName</Label>
|
|
<Input
|
|
id="wehago-caller"
|
|
type="text"
|
|
value={authConfig.callerName || ""}
|
|
onChange={(e) => updateAuthConfig("callerName", e.target.value)}
|
|
placeholder="예: API_gcmsAmaranth40578"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="wehago-token">accessToken</Label>
|
|
<Input
|
|
id="wehago-token"
|
|
type="password"
|
|
value={authConfig.accessToken || ""}
|
|
onChange={(e) => updateAuthConfig("accessToken", e.target.value)}
|
|
placeholder="Bearer 토큰으로 사용됩니다"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="wehago-hash-key">hashKey</Label>
|
|
<Input
|
|
id="wehago-hash-key"
|
|
type="password"
|
|
value={authConfig.hashKey || ""}
|
|
onChange={(e) => updateAuthConfig("hashKey", e.target.value)}
|
|
placeholder="HMAC-SHA256 서명 키"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="wehago-group-seq">groupSeq</Label>
|
|
<Input
|
|
id="wehago-group-seq"
|
|
type="text"
|
|
value={authConfig.groupSeq || ""}
|
|
onChange={(e) => updateAuthConfig("groupSeq", e.target.value)}
|
|
placeholder="예: gcmsAmaranth40578"
|
|
/>
|
|
</div>
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
* 매 요청마다 32자리 transaction-id, Unix timestamp, wehago-sign(HMAC-SHA256(accessToken+transactionId+timestamp+urlPath, hashKey) → Base64)을 자동 생성합니다.
|
|
<br />
|
|
* Wehago/RPS ERP API와 호환되는 인증 헤더(callerName, Authorization, transaction-id, timestamp, groupSeq, wehago-sign)가 자동 부착됩니다.
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{authType === "none" && (
|
|
<div className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
|
|
인증이 필요하지 않은 공개 API입니다.
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|