Gitea CI 빌드에 .env 파일이 포함되지 않아 VITE_SHOW_DEMO_LOGIN이 주입되지 않는 문제 대응. 허용된 호스트에서는 환경변수 없이도 데모 퀵로그인 표시. 허용 호스트: - localhost / 127.0.0.1 (로컬 개발) - kcg-ai-monitoring.gc-si.dev (현재 데모 운영) 실운영 호스트로 전환 시 DEMO_ALLOWED_HOSTS에서 제거 필요. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
|
|
/*
|
|
* 데모 퀵로그인 영역.
|
|
*
|
|
* 표시 조건: VITE_SHOW_DEMO_LOGIN === 'true' (.env 또는 빌드 환경변수)
|
|
* - 로컬 개발: .env.development에 VITE_SHOW_DEMO_LOGIN=true
|
|
* - 배포 환경: .env.production에 VITE_SHOW_DEMO_LOGIN=false (또는 미설정)
|
|
*
|
|
* 데모 계정은 백엔드 DB(kcgaidb)에 실제 BCrypt 해시로 시드되어 있으며,
|
|
* 권한, 로그인 이력, 감사 로그도 동일하게 기록된다.
|
|
* 따라서 클릭 시에도 정상 로그인 플로우를 거쳐 백엔드 인증을 수행한다.
|
|
*/
|
|
|
|
export interface DemoAccount {
|
|
account: string;
|
|
password: string;
|
|
roleLabelKey: string; // i18n 키
|
|
}
|
|
|
|
export const DEMO_ACCOUNTS: DemoAccount[] = [
|
|
{ account: 'admin', password: 'admin1234!', roleLabelKey: 'demo.admin' },
|
|
{ account: 'operator', password: 'oper12345!', roleLabelKey: 'demo.operator' },
|
|
{ account: 'analyst', password: 'anal12345!', roleLabelKey: 'demo.analyst' },
|
|
{ account: 'field', password: 'field1234!', roleLabelKey: 'demo.field' },
|
|
{ account: 'viewer', password: 'view12345!', roleLabelKey: 'demo.viewer' },
|
|
];
|
|
|
|
/**
|
|
* 데모 계정 퀵 로그인 노출 조건:
|
|
* 1. 빌드 환경변수 VITE_SHOW_DEMO_LOGIN=true (로컬 개발 우선)
|
|
* 2. 로컬 개발 호스트 (localhost/127.0.0.1)
|
|
* 3. 데모 배포 호스트 (kcg-ai-monitoring.gc-si.dev — 현재 데모 운영 중이므로 노출)
|
|
*
|
|
* 실운영 호스트로 전환 시 3번 조건을 제거하거나 hostname 목록에서 제외.
|
|
*/
|
|
const DEMO_ALLOWED_HOSTS = [
|
|
'localhost',
|
|
'127.0.0.1',
|
|
'kcg-ai-monitoring.gc-si.dev',
|
|
];
|
|
|
|
export function isDemoLoginEnabled(): boolean {
|
|
if (import.meta.env.VITE_SHOW_DEMO_LOGIN === 'true') return true;
|
|
if (typeof window === 'undefined') return false;
|
|
return DEMO_ALLOWED_HOSTS.includes(window.location.hostname);
|
|
}
|
|
|
|
interface DemoQuickLoginProps {
|
|
onSelect: (account: DemoAccount) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export function DemoQuickLogin({ onSelect, disabled }: DemoQuickLoginProps) {
|
|
const { t } = useTranslation('auth');
|
|
|
|
if (!isDemoLoginEnabled()) return null;
|
|
|
|
return (
|
|
<div className="pt-2 border-t border-border">
|
|
<div className="text-[9px] text-hint text-center mb-2">{t('demo.title')}</div>
|
|
<div className="grid grid-cols-5 gap-1.5">
|
|
{DEMO_ACCOUNTS.map((acct) => (
|
|
<button
|
|
key={acct.account}
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={() => onSelect(acct)}
|
|
className="py-1.5 rounded-md text-[9px] font-medium bg-surface-overlay border border-border text-muted-foreground hover:text-heading hover:bg-switch-background/60 transition-colors whitespace-nowrap disabled:opacity-50"
|
|
>
|
|
{t(acct.roleLabelKey)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|