| <!DOCTYPE html> |
| <html lang="ko" data-theme="dark"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title id="html-title">CertBridge 설치 가이드</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| |
| |
| |
| |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease; |
| } |
| |
| :root { |
| |
| --bg-primary: #ffffff; |
| --bg-secondary: #f8fafc; |
| --bg-card: #ffffff; |
| --bg-glass: rgba(0, 0, 0, 0.015); |
| --bg-hover: rgba(0, 0, 0, 0.025); |
| |
| --text-primary: #1e293b; |
| --text-secondary: #475569; |
| --text-muted: #94a3b8; |
| |
| --accent-blue: #2563eb; |
| --accent-blue-hover: #1d4ed8; |
| --accent-blue-subtle: rgba(37, 99, 235, 0.08); |
| --accent-emerald: #059669; |
| --accent-amber: #d97706; |
| --accent-red: #dc2626; |
| --accent-purple: #7c3aed; |
| |
| --border-subtle: rgba(0, 0, 0, 0.08); |
| --border-active: rgba(37, 99, 235, 0.4); |
| |
| --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.03); |
| --shadow-card-hover: 0 2px 8px rgba(0, 0, 0, 0.06); |
| --shadow-glow: none; |
| |
| --grad: linear-gradient(135deg, #2563eb 0%, #0284c7 100%); |
| --grad-hero: linear-gradient(135deg, rgba(37, 99, 235, 0.05) 0%, rgba(2, 132, 199, 0.05) 100%); |
| --hero-text: #1e293b; |
| --hero-desc: #475569; |
| } |
| |
| [data-theme='dark'] { |
| |
| --bg-primary: #0f1117; |
| --bg-secondary: #1a1d27; |
| --bg-card: #1e2130; |
| --bg-glass: rgba(255, 255, 255, 0.04); |
| --bg-hover: rgba(255, 255, 255, 0.05); |
| |
| --text-primary: #e8eaed; |
| --text-secondary: #9aa0b0; |
| --text-muted: #5f6577; |
| |
| --accent-blue: #3b82f6; |
| --accent-blue-hover: #2563eb; |
| --accent-blue-subtle: rgba(59, 130, 246, 0.12); |
| --accent-emerald: #10b981; |
| --accent-amber: #f59e0b; |
| --accent-red: #ef4444; |
| --accent-purple: #8b5cf6; |
| |
| --border-subtle: rgba(255, 255, 255, 0.07); |
| --border-active: rgba(59, 130, 246, 0.5); |
| |
| --shadow-card: 0 1px 4px rgba(0, 0, 0, 0.2); |
| --shadow-card-hover: 0 4px 16px rgba(0, 0, 0, 0.3); |
| --shadow-glow: 0 0 20px rgba(59, 130, 246, 0.1); |
| |
| --grad: linear-gradient(135deg, #3b82f6 0%, #0ea5e9 100%); |
| --grad-hero: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(14, 165, 233, 0.08) 100%); |
| --hero-text: #e8eaed; |
| --hero-desc: #9aa0b0; |
| } |
| |
| body { |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; |
| background-color: var(--bg-primary); |
| color: var(--text-primary); |
| line-height: 1.6; |
| display: flex; |
| min-height: 100vh; |
| } |
| |
| ::selection { |
| background: var(--accent-blue-subtle); |
| color: var(--accent-blue); |
| } |
| |
| a { |
| color: var(--accent-blue); |
| text-decoration: none; |
| font-weight: 500; |
| } |
| a:hover { |
| text-decoration: underline; |
| } |
| |
| |
| .sidebar { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 260px; |
| height: 100vh; |
| background: var(--bg-secondary); |
| border-right: 1px solid var(--border-subtle); |
| padding: 24px 0; |
| overflow-y: auto; |
| z-index: 100; |
| } |
| |
| .sidebar-logo { |
| padding: 0 24px 20px; |
| border-bottom: 1px solid var(--border-subtle); |
| margin-bottom: 16px; |
| } |
| |
| .sidebar-logo h1 { |
| font-size: 16px; |
| font-weight: 700; |
| color: var(--accent-blue); |
| letter-spacing: -0.3px; |
| font-family: 'Outfit', sans-serif; |
| } |
| |
| .sidebar-logo p { |
| font-size: 11px; |
| color: var(--text-muted); |
| margin-top: 4px; |
| } |
| |
| .sidebar nav a { |
| display: flex; |
| align-items: center; |
| padding: 10px 24px; |
| font-size: 13px; |
| color: var(--text-secondary); |
| transition: all 0.2s; |
| border-left: 3px solid transparent; |
| text-decoration: none; |
| } |
| |
| .sidebar nav a:hover, .sidebar nav a.active { |
| color: var(--accent-blue); |
| background: var(--accent-blue-subtle); |
| border-left-color: var(--accent-blue); |
| } |
| |
| .sidebar nav a .num { |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| width: 20px; |
| height: 20px; |
| border-radius: 4px; |
| background: var(--bg-glass); |
| font-size: 10px; |
| font-weight: 600; |
| margin-right: 10px; |
| color: var(--text-muted); |
| } |
| |
| .sidebar nav a.active .num { |
| background: var(--accent-blue); |
| color: white; |
| } |
| |
| |
| .main { |
| margin-left: 260px; |
| flex: 1; |
| padding: 0; |
| overflow-y: auto; |
| height: 100vh; |
| } |
| |
| |
| body.in-iframe .sidebar { |
| display: none !important; |
| } |
| |
| body.in-iframe .main { |
| margin-left: 0 !important; |
| width: 100% !important; |
| height: 100% !important; |
| } |
| |
| body.in-iframe .content { |
| padding: 32px 24px 60px; |
| max-width: 100%; |
| } |
| |
| |
| .hero { |
| background: var(--grad-hero); |
| padding: 48px 40px; |
| border-bottom: 1px solid var(--border-subtle); |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .hero h1 { |
| font-size: 26px; |
| font-weight: 700; |
| color: var(--hero-text); |
| font-family: 'Outfit', sans-serif; |
| letter-spacing: -0.5px; |
| } |
| |
| .hero p { |
| font-size: 13.5px; |
| color: var(--hero-desc); |
| margin-top: 8px; |
| max-width: 800px; |
| } |
| |
| .hero .badge { |
| display: inline-block; |
| padding: 4px 12px; |
| background: var(--bg-glass); |
| border: 1px solid var(--border-subtle); |
| border-radius: 20px; |
| font-size: 11px; |
| font-weight: 600; |
| color: var(--text-secondary); |
| margin-top: 12px; |
| } |
| |
| |
| .content { |
| padding: 48px; |
| max-width: 1000px; |
| margin: 0 auto; |
| } |
| |
| .section { |
| margin-bottom: 48px; |
| scroll-margin-top: 40px; |
| } |
| |
| h2 { |
| font-size: 18px; |
| font-weight: 700; |
| margin-bottom: 16px; |
| position: relative; |
| padding-left: 12px; |
| color: var(--text-primary); |
| font-family: 'Outfit', sans-serif; |
| } |
| |
| h2::before { |
| content: ''; |
| position: absolute; |
| left: 0; |
| top: 3px; |
| bottom: 3px; |
| width: 3px; |
| border-radius: 1.5px; |
| background: var(--accent-blue); |
| } |
| |
| h3 { |
| font-size: 14px; |
| font-weight: 600; |
| margin: 24px 0 12px; |
| color: var(--accent-blue); |
| } |
| |
| h4 { |
| font-size: 13px; |
| font-weight: 600; |
| margin: 16px 0 8px; |
| color: var(--text-primary); |
| } |
| |
| p, li { |
| margin-bottom: 8px; |
| color: var(--text-secondary); |
| font-size: 13px; |
| line-height: 1.6; |
| } |
| |
| ul, ol { |
| margin: 0 0 16px 20px; |
| } |
| |
| |
| .card { |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| border-radius: var(--radius-lg, 12px); |
| padding: 20px; |
| margin: 16px 0; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .card.tip, .card.info { |
| border-color: rgba(37, 99, 235, 0.2); |
| background: var(--accent-blue-subtle); |
| } |
| |
| .card.tip h4, .card.info h4 { |
| color: var(--accent-blue); |
| margin-top: 0; |
| } |
| |
| .card.warn { |
| border-color: rgba(220, 38, 38, 0.2); |
| background: rgba(220, 38, 38, 0.03); |
| } |
| |
| .card.warn h4 { |
| color: var(--accent-red); |
| margin-top: 0; |
| } |
| |
| |
| table { |
| width: 100%; |
| border-collapse: collapse; |
| margin: 16px 0; |
| border-radius: 8px; |
| overflow: hidden; |
| border: 1px solid var(--border-subtle); |
| } |
| |
| th { |
| background: var(--bg-glass); |
| padding: 10px 14px; |
| text-align: left; |
| font-size: 12px; |
| font-weight: 600; |
| color: var(--text-primary); |
| border-bottom: 1px solid var(--border-subtle); |
| } |
| |
| td { |
| padding: 10px 14px; |
| font-size: 12px; |
| border-top: 1px solid var(--border-subtle); |
| color: var(--text-secondary); |
| background: var(--bg-secondary); |
| } |
| |
| tr:hover td { |
| background: var(--bg-hover); |
| } |
| |
| .role-card { |
| display: grid; |
| grid-template-columns: repeat(3, 1fr); |
| gap: 16px; |
| margin: 24px 0; |
| } |
| |
| .role { |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| border-radius: 12px; |
| padding: 18px; |
| text-align: center; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .role:hover { |
| border-color: var(--accent-blue); |
| } |
| |
| .role .icon { |
| font-size: 24px; |
| margin-bottom: 8px; |
| } |
| |
| .role .name { |
| font-size: 13.5px; |
| font-weight: 700; |
| color: var(--text-primary); |
| margin-bottom: 6px; |
| } |
| |
| .role .desc { |
| font-size: 11.5px; |
| color: var(--text-secondary); |
| line-height: 1.4; |
| } |
| |
| .flow { |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| margin: 16px 0; |
| flex-wrap: wrap; |
| } |
| |
| .flow-step { |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| border-radius: 8px; |
| padding: 8px 12px; |
| font-size: 11.5px; |
| color: var(--text-primary); |
| font-weight: 500; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .flow-arrow { |
| color: var(--accent-blue); |
| font-size: 13px; |
| font-weight: bold; |
| } |
| |
| |
| code { |
| font-family: 'JetBrains Mono', 'SF Mono', monospace; |
| font-size: 11px; |
| background: var(--accent-blue-subtle); |
| padding: 2px 5px; |
| border-radius: 4px; |
| color: var(--accent-blue); |
| } |
| |
| .code-wrap { |
| position: relative; |
| margin: 16px 0; |
| border-radius: 8px; |
| overflow: hidden; |
| border: 1px solid var(--border-subtle); |
| box-shadow: var(--shadow-card); |
| } |
| |
| .code-header { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 6px 14px; |
| background: var(--bg-hover); |
| border-bottom: 1px solid var(--border-subtle); |
| } |
| |
| .code-header span { |
| font-size: 11px; |
| color: var(--text-muted); |
| font-family: 'JetBrains Mono', monospace; |
| } |
| |
| .copy-btn { |
| padding: 2px 8px; |
| border: 1px solid var(--border-subtle); |
| border-radius: 4px; |
| background: var(--bg-secondary); |
| color: var(--text-secondary); |
| cursor: pointer; |
| font-size: 10px; |
| transition: all 0.15s; |
| } |
| |
| .copy-btn:hover { |
| background: var(--accent-blue); |
| color: white; |
| border-color: var(--accent-blue); |
| } |
| |
| pre { |
| background: var(--bg-card); |
| padding: 12px 16px; |
| overflow-x: auto; |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 11.5px; |
| line-height: 1.5; |
| color: var(--text-secondary); |
| } |
| |
| pre .comment { color: var(--text-muted); font-style: italic; } |
| pre .keyword { color: var(--accent-purple); font-weight: 600; } |
| pre .string { color: var(--accent-emerald); } |
| pre .cmd { color: var(--accent-blue); } |
| |
| .menu-path { |
| color: var(--accent-blue); |
| font-weight: 600; |
| } |
| |
| |
| .req-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); |
| gap: 16px; |
| margin: 16px 0; |
| } |
| |
| .req-item { |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| border-radius: 12px; |
| padding: 16px; |
| text-align: center; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .req-item .icon { |
| font-size: 24px; |
| margin-bottom: 6px; |
| } |
| |
| .req-item .label { |
| font-size: 10px; |
| color: var(--text-muted); |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| } |
| |
| .req-item .value { |
| font-size: 13.5px; |
| font-weight: 600; |
| color: var(--text-primary); |
| margin-top: 4px; |
| } |
| |
| .step { |
| display: flex; |
| gap: 16px; |
| margin: 16px 0; |
| padding: 16px; |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| border-radius: 12px; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .step-num { |
| flex-shrink: 0; |
| width: 28px; |
| height: 28px; |
| border-radius: 6px; |
| background: var(--grad); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-weight: 700; |
| font-size: 12px; |
| color: #fff; |
| } |
| |
| .step-body h4 { |
| margin: 0 0 4px; |
| color: var(--text-primary); |
| } |
| |
| .step-body p { |
| margin: 0; |
| color: var(--text-secondary); |
| font-size: 12.5px; |
| } |
| |
| .port-badge { |
| display: inline-block; |
| padding: 2px 6px; |
| border-radius: 4px; |
| background: var(--accent-blue-subtle); |
| color: var(--accent-blue); |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 11px; |
| font-weight: 600; |
| } |
| |
| .top-toolbar { |
| position: absolute; |
| top: 20px; |
| right: 20px; |
| z-index: 10; |
| display: flex; |
| gap: 8px; |
| } |
| |
| .lang-selector { |
| background: var(--bg-secondary); |
| border: 1px solid var(--border-subtle); |
| padding: 4px 10px; |
| border-radius: 6px; |
| color: var(--text-secondary); |
| font-size: 11.5px; |
| outline: none; |
| cursor: pointer; |
| box-shadow: var(--shadow-card); |
| } |
| |
| .lang-selector:hover { |
| border-color: var(--accent-blue); |
| color: var(--text-primary); |
| } |
| |
| @media (max-width: 900px) { |
| .sidebar { display: none; } |
| .main { margin-left: 0; } |
| .content { padding: 24px; } |
| .hero { padding: 32px 24px; } |
| .hero h1 { font-size: 22px; } |
| } |
| |
| @media print { |
| .sidebar { display: none; } |
| .main { margin-left: 0; } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <aside class="sidebar"> |
| <div class="sidebar-logo"> |
| <h1>📋 CertBridge</h1> |
| <p data-i18n="sidebar.ver">설치 가이드 v0.1.0</p> |
| </div> |
| <nav id="nav"> |
| <a href="#requirements" class="active"><span class="num">1</span><span data-i18n="nav.req">시스템 요구사항</span></a> |
| <a href="#server-install"><span class="num">2</span><span data-i18n="nav.server">서버 설치 (Ubuntu)</span></a> |
| <a href="#client-install"><span class="num">3</span><span data-i18n="nav.client">클라이언트 설치 (Windows)</span></a> |
| <a href="#network"><span class="num">4</span><span data-i18n="nav.network">네트워크 구성</span></a> |
| <a href="#initial-setup"><span class="num">5</span><span data-i18n="nav.setup">초기 설정</span></a> |
| <a href="#troubleshooting"><span class="num">6</span><span data-i18n="nav.trouble">문제 해결</span></a> |
| </nav> |
| </aside> |
|
|
| <div class="main"> |
| <div class="top-toolbar"> |
| <select id="lang-select" class="lang-selector" onchange="changeLanguage(this.value)"> |
| <option value="ko">한국어</option> |
| <option value="en">English</option> |
| <option value="zh">中文</option> |
| <option value="ja">日本語</option> |
| <option value="de">Deutsch</option> |
| <option value="fr">Français</option> |
| <option value="es">Español</option> |
| <option value="pt">Português</option> |
| <option value="hu">Magyar</option> |
| </select> |
| </div> |
|
|
| <div class="hero"> |
| <h1 data-i18n="hero.title">CertBridge 설치 및 셋업 가이드</h1> |
| <p data-i18n="hero.desc">AI 성적서 자동 파싱 및 레거시 통합 시스템을 Ubuntu 서버와 Windows 클라이언트에 구축하는 상세 프로토콜을 설명합니다.</p> |
| <span class="badge" data-i18n="hero.badge">📦 v0.1.0 · Ubuntu 22.04+ · Windows 10+</span> |
| </div> |
|
|
| <div class="content"> |
|
|
| |
| <section class="section" id="requirements"> |
| <h2 data-i18n="req.title">1. 시스템 요구사항</h2> |
| |
| <h3 data-i18n="req.server_title">🖥️ 서버 하드웨어 권장사양 (Ubuntu OS)</h3> |
| <div class="req-grid"> |
| <div class="req-item"><div class="icon">🐧</div><div class="label" data-i18n="req.os">OS</div><div class="value">Ubuntu 22.04 LTS+</div></div> |
| <div class="req-item"><div class="icon">🧠</div><div class="label" data-i18n="req.ram">RAM</div><div class="value">4GB+ (8GB 권장)</div></div> |
| <div class="req-item"><div class="icon">💾</div><div class="label" data-i18n="req.disk">Disk Space</div><div class="value">20GB+ (SSD 권장)</div></div> |
| <div class="req-item"><div class="icon">🌐</div><div class="label" data-i18n="req.net">Network</div><div class="value">정적 IP / 외부 아웃바운드</div></div> |
| </div> |
|
|
| <h3 data-i18n="req.client_title">💻 클라이언트 실행 환경 (Windows)</h3> |
| <div class="req-grid"> |
| <div class="req-item"><div class="icon">🪟</div><div class="label" data-i18n="req.os">OS</div><div class="value">Windows 10 / 11 (64bit)</div></div> |
| <div class="req-item"><div class="icon">🧠</div><div class="label" data-i18n="req.ram">RAM</div><div class="value">2GB+ (4GB 권장)</div></div> |
| <div class="req-item"><div class="icon">🌐</div><div class="label" data-i18n="req.net">Network</div><div class="value">중앙 서버 8090 포트 접근</div></div> |
| </div> |
|
|
| <h3 data-i18n="req.software">🔧 필수 인프라 소프트웨어 (서버 측)</h3> |
| <table> |
| <thead> |
| <tr> |
| <th data-i18n="req.tb_soft">소프트웨어</th> |
| <th data-i18n="req.tb_ver">최소 버전</th> |
| <th data-i18n="req.tb_usage">용도</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>Docker Engine</td> |
| <td>24.0.0+</td> |
| <td data-i18n="req.tb_docker_desc">컨테이너 기반 데이터베이스 및 캐시 오케스트레이션</td> |
| </tr> |
| <tr> |
| <td>Docker Compose</td> |
| <td>v2.20.0+</td> |
| <td data-i18n="req.tb_compose_desc">멀티 컨테이너 인프라 스택 통합 실행</td> |
| </tr> |
| <tr> |
| <td>Node.js Runtime</td> |
| <td>20.x LTS+</td> |
| <td data-i18n="req.tb_node_desc">CertBridge Express API 코어 서버 구동</td> |
| </tr> |
| </tbody> |
| </table> |
| </section> |
|
|
| |
| <section class="section" id="server-install"> |
| <h2 data-i18n="server.title">2. 서버 설치 (Ubuntu)</h2> |
| |
| <h3 data-i18n="server.auto">2-1. 자동 쉘 스크립트 설치 (권장)</h3> |
| <p data-i18n="server.auto_desc">제공된 setup.sh 파일을 활용해 도커 엔진, 환경변수, 데이터베이스, systemd 서비스 등록을 한 번에 자동으로 완료합니다.</p> |
|
|
| <div class="code-wrap"> |
| <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre><span class="comment"># 1. 서버 패키지 아카이브 다운로드</span> |
| <span class="cmd">wget</span> https://huggingface.co/VanyaJ/certbridge/resolve/main/certbridge-server-v0.1.0-ubuntu.tar.gz |
|
|
| <span class="comment"># 2. 파일 압축 해제 및 디렉토리 이동</span> |
| <span class="cmd">tar</span> -xzf certbridge-server-v0.1.0-ubuntu.tar.gz |
| <span class="cmd">cd</span> certbridge-server-ubuntu |
|
|
| <span class="comment"># 3. 설치 권한 부여 및 쉘 실행</span> |
| <span class="cmd">chmod</span> +x setup.sh |
| <span class="cmd">sudo</span> ./setup.sh</pre> |
| </div> |
|
|
| <h3 data-i18n="server.manual">2-2. 단계별 수동 설치 프로토콜</h3> |
| |
| <h4 data-i18n="server.d_setup">Docker 설치 및 실행 환경 구성</h4> |
| <div class="code-wrap"> |
| <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre><span class="comment"># 패키지 업데이트 및 GPG 키 추가</span> |
| <span class="cmd">sudo apt-get</span> update |
| <span class="cmd">sudo apt-get install -y</span> ca-certificates curl gnupg |
| <span class="cmd">sudo install -m 0755 -d</span> /etc/apt/keyrings |
| <span class="cmd">curl -fsSL</span> https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg |
|
|
| <span class="comment"># 레포지토리 정보 추가 및 도커 코어 패키지 설치</span> |
| <span class="cmd">echo</span> "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null |
| <span class="cmd">sudo apt-get</span> update |
| <span class="cmd">sudo apt-get install -y</span> docker-ce docker-ce-cli containerd.io docker-compose-plugin</pre> |
| </div> |
|
|
| <h4 data-i18n="server.compose">도커 컴포즈 기반 인프라 구성요소 실행</h4> |
| <p data-i18n="server.compose_desc">PostgreSQL, Redis, MinIO 오브젝트 스토리지 및 Neo4j 그래프 데이터베이스를 하나의 백그라운드 스택으로 띄웁니다.</p> |
| <div class="code-wrap"> |
| <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre><span class="comment"># server 디렉토리 내부 docker-compose 파일 실행</span> |
| <span class="cmd">sudo docker compose</span> up -d |
|
|
| <span class="comment"># 기동 상태 정상화 확인</span> |
| <span class="cmd">sudo docker compose</span> ps</pre> |
| </div> |
|
|
| <h4 data-i18n="server.env_setup">API 환경 파일 구성 (.env)</h4> |
| <div class="code-wrap"> |
| <div class="code-header"><span>.env</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre># Central Data Store Connections |
| DATABASE_URL=postgres://n8n:n8n_password@localhost:5432/certbridge |
| REDIS_URL=redis://localhost:6379/2 |
|
|
| # File Storage Configuration |
| MINIO_ENDPOINT=localhost:9000 |
| MINIO_ACCESS_KEY=minioadmin |
| MINIO_SECRET_KEY=minioadmin |
| MINIO_BUCKET=certbridge |
|
|
| # Neo4j Graph DB Config |
| NEO4J_URI=bolt://localhost:7687 |
| NEO4J_USER=neo4j |
| NEO4J_PASSWORD=certbridge123 |
|
|
| # Core Express API Config |
| API_PORT=8090 |
| JWT_SECRET=your-premium-secret-key-change-in-production</pre> |
| </div> |
|
|
| <div class="card warn"> |
| <h4 data-i18n="server.warn_title">⚠️ 필수 보안 주의사항</h4> |
| <p data-i18n="server.warn_desc">운영 환경 배포 시, <code>JWT_SECRET</code> 암호화 키와 기본 데이터베이스 패스워드 및 MinIO ACCESS/SECRET 키를 난수 형태로 반드시 교체해 적용하십시오.</p> |
| </div> |
|
|
| <h4 data-i18n="server.api_start">API 서버 의존성 설치 및 기동</h4> |
| <div class="code-wrap"> |
| <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre><span class="comment"># server/api 폴더로 이동 후 모듈 다운로드</span> |
| <span class="cmd">npm</span> install --production |
|
|
| <span class="comment"># API 서버 데몬 모드로 수동 기동</span> |
| <span class="cmd">source</span> .env && <span class="cmd">node</span> server.js |
|
|
| <span class="comment"># 헬스체크 정상 수신 확인</span> |
| <span class="cmd">curl</span> http://localhost:8090/health</pre> |
| </div> |
| </section> |
|
|
| |
| <section class="section" id="client-install"> |
| <h2 data-i18n="client.title">3. 클라이언트 설치 (Windows)</h2> |
| |
| <div class="step"> |
| <div class="step-num">1</div> |
| <div class="step-body"> |
| <h4 data-i18n="client.s1_title">설치 파일 패키지 다운로드</h4> |
| <p data-i18n="client.s1_desc">공식 릴리즈 보관소(HuggingFace 또는 회사 CDN망)에서 <code>CertBridge_0.1.0_x64-setup.exe</code>(자동 빌드 본)를 안전하게 받아옵니다.</p> |
| </div> |
| </div> |
| |
| <div class="step"> |
| <div class="step-num">2</div> |
| <div class="step-body"> |
| <h4 data-i18n="client.s2_title">설치 마법사 진행 및 언어 타겟팅</h4> |
| <p data-i18n="client.s2_desc">설치 마법사 단계에서 원하는 언어를 기본적으로 설정(한글/영어 자동 매칭)한 후, 지정된 경로에 클라이언트 바이너리를 빌드 설치합니다.</p> |
| </div> |
| </div> |
|
|
| <div class="step"> |
| <div class="step-num">3</div> |
| <div class="step-body"> |
| <h4 data-i18n="client.s3_title">중앙 엔드포인트 연결 테스트</h4> |
| <p data-i18n="client.s3_desc">바탕화면 단축 아이콘을 통해 실행 후, 우측 아래의 <strong>설정 아이콘(⚙️)</strong>을 클릭해 중앙에 개방된 API 서버 IP 및 도메인을 입력하여 테스트 통신합니다: <code>http://<중앙서버_IP>:8090</code></p> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="section" id="network"> |
| <h2 data-i18n="net.title">4. 네트워크 및 포트 구성</h2> |
| <h3 data-i18n="net.ports">인프라 시스템 사용 포트 현황</h3> |
| <table> |
| <thead> |
| <tr> |
| <th data-i18n="net.tb_serv">연동 서비스</th> |
| <th data-i18n="net.tb_port">기본 포트</th> |
| <th data-i18n="net.tb_proto">프로토콜</th> |
| <th data-i18n="net.tb_desc">설명</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td>CertBridge API Gateway</td> |
| <td><span class="port-badge">8090</span></td> |
| <td>HTTP</td> |
| <td data-i18n="net.tb_p1">코어 클라이언트 REST & 하트비트 연결 창구 (인바운드 허용 필수)</td> |
| </tr> |
| <tr> |
| <td>Neo4j Bolt Driver</td> |
| <td><span class="port-badge">7687</span></td> |
| <td>TCP / Bolt</td> |
| <td data-i18n="net.tb_p2">품질 지식 온톨로지 고속 커넥터 포트</td> |
| </tr> |
| <tr> |
| <td>MinIO Storage API</td> |
| <td><span class="port-badge">9000</span></td> |
| <td>HTTP</td> |
| <td data-i18n="net.tb_p3">성적서 PDF 물리 원본 파일 읽기/쓰기 스토리지 API</td> |
| </tr> |
| <tr> |
| <td>n8n Automation Engine</td> |
| <td><span class="port-badge">5678</span></td> |
| <td>HTTP</td> |
| <td data-i18n="net.tb_p4">워크플로우 자동화 파이프라인 트리거 엔드포인트</td> |
| </tr> |
| </tbody> |
| </table> |
|
|
| <h3 data-i18n="net.firewall">Ubuntu 방화벽 (UFW) 설정 스크립트</h3> |
| <div class="code-wrap"> |
| <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">복사</button></div> |
| <pre><span class="comment"># 기본 포트 개방</span> |
| <span class="cmd">sudo ufw</span> allow 8090/tcp |
|
|
| <span class="comment"># 보안 권고: 데이터 관리자 IP 대역만 Neo4j, MinIO 콘솔 허용</span> |
| <span class="cmd">sudo ufw</span> allow from 192.168.10.0/24 to any port 7474 |
| <span class="cmd">sudo ufw</span> allow from 192.168.10.0/24 to any port 9001</pre> |
| </div> |
| </section> |
|
|
| |
| <section class="section" id="initial-setup"> |
| <h2 data-i18n="setup.title">5. 중앙 관리 환경 초기화</h2> |
| <h3 data-i18n="setup.admin">5-1. 최초 관리자 계정 정보</h3> |
| <p data-i18n="setup.admin_desc">설치가 정상적으로 마쳐지면 데이터베이스 시드 스크립트를 통해 아래 관리자 레코드가 생성됩니다:</p> |
| <table> |
| <thead> |
| <tr> |
| <th data-i18n="setup.tb_col">설정 정보</th> |
| <th data-i18n="setup.tb_val">지정된 기본 값</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td data-i18n="start.tb_id">아이디</td> |
| <td><code>admin</code></td> |
| </tr> |
| <tr> |
| <td data-i18n="start.tb_pw">비밀번호</td> |
| <td><code>admin123</code></td> |
| </tr> |
| <tr> |
| <td data-i18n="setup.tb_role">등급</td> |
| <td><code>admin</code></td> |
| </tr> |
| </tbody> |
| </table> |
|
|
| <h3 data-i18n="setup.group">5-2. 공장 그룹 및 로컬 프로파일 할당</h3> |
| <p data-i18n="setup.group_desc">어드민 로그인 완료 후 <strong>[그룹 관리]</strong> 탭으로 진입하여 새 조직을 추가합니다. 본사 중앙 서버를 유지하면서 각 로컬 공장(예: 청주공장, 헝가리공장, 미국공장)마다 로컬 DB 접속 문자열 및 로컬 MinIO/Neo4j 정보를 다르게 입력해 데이터를 완벽히 격리할 수 있습니다.</p> |
| </section> |
|
|
| |
| <section class="section" id="troubleshooting"> |
| <h2 data-i18n="trouble.title">6. 문제 해결 (Troubleshooting)</h2> |
| |
| <div class="card warn"> |
| <h4 data-i18n="trouble.q1">🔴 Docker 컨테이너 구동 시 Permission Denied 발생</h4> |
| <p data-i18n="trouble.a1">사용자 계정이 docker 권한 그룹에 포함되지 않아 발생합니다. <code>sudo usermod -aG docker $USER</code> 명령을 입력한 후, 쉘 터미널 세션을 완전히 종료했다가 다시 로그인해 시작하십시오.</p> |
| </div> |
|
|
| <div class="card warn"> |
| <h4 data-i18n="trouble.q2">🔴 "ECONNREFUSED" 서버 응답 에러 수신</h4> |
| <p data-i18n="trouble.a2">중앙 서버 측의 API 실행 데몬 프로세스가 기동되지 않았거나 포트가 충돌한 경우입니다. <code>sudo systemctl status certbridge</code> 상태 조회를 통해 systemd 서비스 생존 유무를 검사하십시오.</p> |
| </div> |
|
|
| <div class="card warn"> |
| <h4 data-i18n="trouble.q3">🟡 Windows SmartScreen 차단 팝업이 출력됩니다.</h4> |
| <p data-i18n="trouble.a3">본 패키지는 도메인 프라이빗 개발 본으로 코드 사이닝(Code Signing) 서명서가 결여되어 있습니다. 팝업 하단의 "추가 정보"를 클릭한 다음, 나타난 "실행" 버튼을 안심하고 클릭해 셋업을 완수해 주시기 바랍니다.</p> |
| </div> |
| </section> |
|
|
| </div> |
| </div> |
|
|
| <script> |
| |
| function syncTheme() { |
| let theme = "light"; |
| try { |
| if (window.parent && window.parent.document && window.parent.document.documentElement) { |
| theme = window.parent.document.documentElement.getAttribute("data-theme") || "light"; |
| } else { |
| theme = localStorage.getItem("certbridge_theme") || "light"; |
| } |
| } catch (e) { |
| theme = localStorage.getItem("certbridge_theme") || "light"; |
| } |
| |
| if (theme === "dark") { |
| document.documentElement.setAttribute("data-theme", "dark"); |
| } else { |
| document.documentElement.removeAttribute("data-theme"); |
| } |
| } |
| syncTheme(); |
| |
| try { |
| if (window.parent && window.parent.document && window.parent.document.documentElement) { |
| const observer = new MutationObserver((mutations) => { |
| mutations.forEach((mutation) => { |
| if (mutation.type === "attributes" && mutation.attributeName === "data-theme") { |
| syncTheme(); |
| } |
| }); |
| }); |
| observer.observe(window.parent.document.documentElement, { attributes: true }); |
| } |
| } catch (e) { |
| setInterval(syncTheme, 1000); |
| } |
| |
| |
| if (window.self !== window.top) { |
| document.body.classList.add('in-iframe'); |
| } |
| |
| |
| const i18n = { |
| ko: { |
| "sidebar.ver": "설치 가이드 v0.1.0", |
| "nav.req": "시스템 요구사항", |
| "nav.server": "서버 설치 (Ubuntu)", |
| "nav.client": "클라이언트 설치 (Windows)", |
| "nav.network": "네트워크 구성", |
| "nav.setup": "초기 설정", |
| "nav.trouble": "문제 해결", |
| "hero.title": "CertBridge 설치 및 셋업 가이드", |
| "hero.desc": "AI 성적서 자동 파싱 및 레거시 통합 시스템을 Ubuntu 서버와 Windows 클라이언트에 구축하는 상세 프로토콜을 설명합니다.", |
| "hero.badge": "📦 v0.1.0 · Ubuntu 22.04+ · Windows 10+", |
| "req.title": "1. 시스템 요구사항", |
| "req.server_title": "🖥️ 서버 하드웨어 권장사양 (Ubuntu OS)", |
| "req.os": "운영체제", |
| "req.ram": "메모리", |
| "req.disk": "디바이스 용량", |
| "req.net": "네트워크망", |
| "req.client_title": "💻 클라이언트 실행 환경 (Windows)", |
| "req.software": "🔧 필수 인프라 소프트웨어 (서버 측)", |
| "req.tb_soft": "소프트웨어", |
| "req.tb_ver": "최소 버전", |
| "req.tb_usage": "용도", |
| "req.tb_docker_desc": "컨테이너 기반 데이터베이스 및 캐시 오케스트레이션", |
| "req.tb_compose_desc": "멀티 컨테이너 인프라 스택 통합 실행", |
| "req.tb_node_desc": "CertBridge Express API 코어 서버 구동", |
| "server.title": "2. 서버 설치 (Ubuntu)", |
| "server.auto": "2-1. 자동 쉘 스크립트 설치 (권장)", |
| "server.auto_desc": "제공된 setup.sh 파일을 활용해 도커 엔진, 환경변수, 데이터베이스, systemd 서비스 등록을 한 번에 자동으로 완료합니다.", |
| "server.manual": "2-2. 단계별 수동 설치 프로토콜", |
| "server.d_setup": "Docker 설치 및 실행 환경 구성", |
| "server.compose": "도커 컴포즈 기반 인프라 구성요소 실행", |
| "server.compose_desc": "PostgreSQL, Redis, MinIO 오브젝트 스토리지 및 Neo4j 그래프 데이터베이스를 하나의 백그라운드 스택으로 띄웁니다.", |
| "server.env_setup": "API 환경 파일 구성 (.env)", |
| "server.warn_title": "⚠️ 필수 보안 주의사항", |
| "server.warn_desc": "운영 환경 배포 시, JWT_SECRET 암호화 키와 기본 데이터베이스 패스워드 및 MinIO ACCESS/SECRET 키를 난수 형태로 반드시 교체해 적용하십시오.", |
| "server.api_start": "API 서버 의존성 설치 및 기동", |
| "client.title": "3. 클라이언트 설치 (Windows)", |
| "client.s1_title": "설치 파일 패키지 다운로드", |
| "client.s1_desc": "공식 릴리즈 보관소(HuggingFace 또는 회사 CDN망)에서 CertBridge_0.1.0_x64-setup.exe(자동 빌드 본)를 안전하게 받아옵니다.", |
| "client.s2_title": "설치 마법사 진행 및 언어 타겟팅", |
| "client.s2_desc": "설치 마법사 단계에서 원하는 언어를 기본적으로 설정(한글/영어 자동 매칭)한 후, 지정된 경로에 클라이언트 바이너리를 빌드 설치합니다.", |
| "client.s3_title": "중앙 엔드포인트 연결 테스트", |
| "client.s3_desc": "바탕화면 단축 아이콘을 통해 실행 후, 우측 아래의 설정 아이콘(⚙️)을 클릭해 중앙에 개방된 API 서버 IP 및 도메인을 입력하여 테스트 통신합니다: http://<중앙서버_IP>:8090", |
| "net.title": "4. 네트워크 및 포트 구성", |
| "net.ports": "인프라 시스템 사용 포트 현황", |
| "net.tb_serv": "연동 서비스", |
| "net.tb_port": "기본 포트", |
| "net.tb_proto": "프로토콜", |
| "net.tb_desc": "설명", |
| "net.tb_p1": "코어 클라이언트 REST & 하트비트 연결 창구 (인바운드 허용 필수)", |
| "net.tb_p2": "품질 지식 온톨로지 고속 커넥터 포트", |
| "net.tb_p3": "성적서 PDF 물리 원본 파일 읽기/쓰기 스토리지 API", |
| "net.tb_p4": "워크플로우 자동화 파이프라인 트리거 엔드포인트", |
| "net.firewall": "Ubuntu 방화벽 (UFW) 설정 스크립트", |
| "setup.title": "5. 중앙 관리 환경 초기화", |
| "setup.admin": "5-1. 최초 관리자 계정 정보", |
| "setup.admin_desc": "설치가 정상적으로 마쳐지면 데이터베이스 시드 스크립트를 통해 아래 관리자 레코드가 생성됩니다:", |
| "setup.tb_col": "설정 정보", |
| "setup.tb_val": "지정된 기본 값", |
| "setup.tb_role": "등급", |
| "setup.group": "5-2. 공장 그룹 및 로컬 프로파일 할당", |
| "setup.group_desc": "어드민 로그인 완료 후 [그룹 관리] 탭으로 진입하여 새 조직을 추가합니다. 본사 중앙 서버를 유지하면서 각 로컬 공장(예: 청주공장, 헝가리공장, 미국공장)마다 로컬 DB 접속 문자열 및 로컬 MinIO/Neo4j 정보를 다르게 입력해 데이터를 완벽히 격리할 수 있습니다.", |
| "trouble.title": "6. 문제 해결 (Troubleshooting)", |
| "trouble.q1": "🔴 Docker 컨테이너 구동 시 Permission Denied 발생", |
| "trouble.a1": "사용자 계정이 docker 권한 그룹에 포함되지 않아 발생합니다. sudo usermod -aG docker $USER 명령을 입력한 후, 쉘 터미널 세션을 완전히 종료했다가 다시 로그인해 시작하십시오.", |
| "trouble.q2": "🔴 \"ECONNREFUSED\" 서버 응답 에러 수신", |
| "trouble.a2": "중앙 서버 측의 API 실행 데몬 프로세스가 기동되지 않았거나 포트가 충돌한 경우입니다. sudo systemctl status certbridge 상태 조회를 통해 systemd 서비스 생존 유무를 검사하십시오.", |
| "trouble.q3": " Windows SmartScreen 차단 팝업이 출력됩니다.", |
| "trouble.a3": "본 패키지는 도메인 프라이빗 개발 본으로 코드 사이닝(Code Signing) 서명서가 결여되어 있습니다. 팝업 하단의 \"추가 정보\"를 클릭한 다음, 나타난 \"실행\" 버튼을 안심하고 클릭해 셋업을 완수해 주시기 바랍니다." |
| }, |
| en: { |
| "sidebar.ver": "Installation Guide v0.1.0", |
| "nav.req": "System Requirements", |
| "nav.server": "Server Setup (Ubuntu)", |
| "nav.client": "Client Setup (Windows)", |
| "nav.network": "Network Config", |
| "nav.setup": "Initial Config", |
| "nav.trouble": "Troubleshooting", |
| "hero.title": "CertBridge Server Deployment Guide", |
| "hero.desc": "Step-by-step instructions to configure the central system environment on an Ubuntu server and set up Windows desktop clients.", |
| "hero.badge": "📦 v0.1.0 · Ubuntu 22.04+ · Windows 10+", |
| "req.title": "1. System Requirements", |
| "req.server_title": "🖥️ Server Configuration (Ubuntu OS)", |
| "req.os": "Operating System", |
| "req.ram": "RAM", |
| "req.disk": "Disk Space", |
| "req.net": "Network Connection", |
| "req.client_title": "💻 Client Environment (Windows)", |
| "req.software": "🔧 Infrastructure Prerequisites (Server)", |
| "req.tb_soft": "Software", |
| "req.tb_ver": "Min Version", |
| "req.tb_usage": "Usage", |
| "req.tb_docker_desc": "Containerized databases and queue storage orchestration", |
| "req.tb_compose_desc": "Multi-container environment running under unified stacks", |
| "req.tb_node_desc": "CertBridge core Express API gateway server execution", |
| "server.title": "2. Server Installation (Ubuntu)", |
| "server.auto": "2-1. Automatic Shell Script Installation (Recommended)", |
| "server.auto_desc": "Use setup.sh to automatically configure Docker, generate required secrets, connect databases, and register the systemd background service.", |
| "server.manual": "2-2. Manual Step-by-Step Installation", |
| "server.d_setup": "Install Docker Engine", |
| "server.compose": "Launch Multi-Container Infrastructure Stack", |
| "server.compose_desc": "Launch PostgreSQL, Redis, MinIO object storage, and Neo4j graph database as a single background service stack.", |
| "server.env_setup": "API Configuration File (.env)", |
| "server.warn_title": "⚠️ Production Security Notice", |
| "server.warn_desc": "Make sure to change JWT_SECRET keys, default database passwords, and MinIO root credential values before deploying in a production network.", |
| "server.api_start": "Install Dependencies and Start API Gateway", |
| "client.title": "3. Desktop Client Setup (Windows)", |
| "client.s1_title": "Download Installer Package", |
| "client.s1_desc": "Get the official desktop application package (e.g. CertBridge_0.1.0_x64-setup.exe) from HuggingFace or your localized internal CDN.", |
| "client.s2_title": "Install & Choose Language Profile", |
| "client.s2_desc": "Follow the wizard setup steps, verify the target path, select your default active language, and complete the installation.", |
| "client.s3_title": "Central Connection Handshake Test", |
| "client.s3_desc": "Open the application, click the Settings (⚙️) icon, and enter the active central API server endpoint address: http://<server_ip>:8090", |
| "net.title": "4. Network & Ports Configurations", |
| "net.ports": "System Port Allocations", |
| "net.tb_serv": "Linked Service", |
| "net.tb_port": "Default Port", |
| "net.tb_proto": "Protocol", |
| "net.tb_desc": "Description", |
| "net.tb_p1": "Core client REST & Heartbeat channel (Inbound access required)", |
| "net.tb_p2": "Neo4j Bolt protocol high-speed graph driver interface", |
| "net.tb_p3": "S3-compatible API for original PDF quality certificate storage", |
| "net.tb_p4": "n8n automation pipeline webhook gateway", |
| "net.firewall": "Ubuntu Firewall (UFW) Configuration Script", |
| "setup.title": "5. Initial Admin Configuration", |
| "setup.admin": "5-1. Default Administrator Credentials", |
| "setup.admin_desc": "The DB seeding script creates the default admin user account as follows:", |
| "setup.tb_col": "Property", |
| "setup.tb_val": "Default Value", |
| "setup.tb_role": "Role", |
| "setup.group": "5-2. Group Isolation & Local Infrastructure Profiles", |
| "setup.group_desc": "Log in as administrator and go to the Groups tab to register your factory groups. This allows you to utilize completely separated database credentials, MinIO buckets, and Neo4j connections per factory group while using a single core central system.", |
| "trouble.title": "6. Troubleshooting Guidelines", |
| "trouble.q1": "🔴 Docker: Permission Denied Error", |
| "trouble.a1": "Add your user account to the docker permission group using: sudo usermod -aG docker $USER. Restart your terminal session and rerun setup.", |
| "trouble.q2": "🔴 'ECONNREFUSED' Connection Error", |
| "trouble.a2": "Check if the API daemon process is running on port 8090. Run: sudo systemctl status certbridge to verify the systemd background state.", |
| "trouble.q3": "🟡 Windows SmartScreen Popup Block Warning", |
| "trouble.a3": "This is normal for dev packages that lack a certified code-signing certificate. Click 'More Info' and choose 'Run Anyway' to complete the installation." |
| } |
| }; |
| |
| function changeLanguage(lang) { |
| document.documentElement.lang = lang; |
| localStorage.setItem("certbridge_language", lang); |
| document.getElementById("lang-select").value = lang; |
| |
| |
| const elements = document.querySelectorAll("[data-i18n]"); |
| elements.forEach(el => { |
| const key = el.getAttribute("data-i18n"); |
| if (i18n[lang] && i18n[lang][key]) { |
| el.innerHTML = i18n[lang][key]; |
| } else if (i18n["en"] && i18n["en"][key]) { |
| el.innerHTML = i18n["en"][key]; |
| } |
| }); |
| |
| |
| if (lang === "ko") { |
| document.getElementById("html-title").innerText = "CertBridge 설치 및 셋업 가이드"; |
| } else if (lang === "zh") { |
| document.getElementById("html-title").innerText = "CertBridge 安装与配置指南"; |
| } else if (lang === "ja") { |
| document.getElementById("html-title").innerText = "CertBridge 構築・設定ガイド"; |
| } else if (lang === "hu") { |
| document.getElementById("html-title").innerText = "CertBridge Szervertelepítési Útmutató"; |
| } else if (lang === "de") { |
| document.getElementById("html-title").innerText = "CertBridge Server-Bereitstellungshandbuch"; |
| } else if (lang === "fr") { |
| document.getElementById("html-title").innerText = "Guide de déploiement serveur CertBridge"; |
| } else if (lang === "es") { |
| document.getElementById("html-title").innerText = "Guía de despliegue de servidor CertBridge"; |
| } else if (lang === "pt") { |
| document.getElementById("html-title").innerText = "Guia de implantação de servidor CertBridge"; |
| } else { |
| document.getElementById("html-title").innerText = "CertBridge Server Deployment Guide"; |
| } |
| |
| console.log(`🌍 Setup Guide Language updated to: ${lang}`); |
| } |
| |
| |
| document.addEventListener("DOMContentLoaded", () => { |
| const getLang = () => localStorage.getItem("certbridge_language") || "ko"; |
| changeLanguage(getLang()); |
| |
| |
| window.addEventListener("storage", (e) => { |
| if (e.key === "certbridge_language") { |
| changeLanguage(e.newValue || "ko"); |
| } |
| }); |
| |
| |
| setInterval(() => { |
| const currentLang = document.documentElement.lang; |
| const targetLang = getLang(); |
| if (currentLang !== targetLang) { |
| changeLanguage(targetLang); |
| } |
| }, 500); |
| |
| |
| const sections = document.querySelectorAll('.section'); |
| const navLinks = document.querySelectorAll('#nav a'); |
| const observer = new IntersectionObserver(entries => { |
| entries.forEach(e => { |
| if (e.isIntersecting) { |
| navLinks.forEach(l => l.classList.remove('active')); |
| const id = e.target.id; |
| const link = document.querySelector(`#nav a[href="#${id}"]`); |
| if (link) link.classList.add('active'); |
| } |
| }); |
| }, { threshold: 0.2 }); |
| sections.forEach(s => observer.observe(s)); |
| }); |
| |
| |
| function copyCode(btn) { |
| const pre = btn.closest('.code-wrap').querySelector('pre'); |
| navigator.clipboard.writeText(pre.textContent).then(() => { |
| const lang = document.documentElement.lang || "ko"; |
| const msg = lang === "ko" ? "복사됨!" : "Copied!"; |
| const originalText = btn.textContent; |
| btn.textContent = msg; |
| setTimeout(() => { |
| btn.textContent = originalText; |
| }, 2000); |
| }); |
| } |
| </script> |
| </body> |
| </html> |
|
|