VanyaJ commited on
Commit
29ee5d7
ยท
verified ยท
1 Parent(s): 7054c4a

Upload release asset: installation-guide.html

Browse files
Files changed (1) hide show
  1. installation-guide.html +1213 -0
installation-guide.html ADDED
@@ -0,0 +1,1213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title id="html-title">CertBridge ์„ค์น˜ ๊ฐ€์ด๋“œ</title>
7
+ <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">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ /* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
11
+ CertBridge Guide Design System
12
+ Dual Theme Sync (Light + Dark)
13
+ Premium Modern Style
14
+ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */
15
+ * {
16
+ margin: 0;
17
+ padding: 0;
18
+ box-sizing: border-box;
19
+ transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
20
+ }
21
+
22
+ :root {
23
+ /* Light Theme (Default) */
24
+ --bg-primary: #ffffff;
25
+ --bg-secondary: #f8fafc;
26
+ --bg-card: #ffffff;
27
+ --bg-glass: rgba(0, 0, 0, 0.015);
28
+ --bg-hover: rgba(0, 0, 0, 0.025);
29
+
30
+ --text-primary: #1e293b;
31
+ --text-secondary: #475569;
32
+ --text-muted: #94a3b8;
33
+
34
+ --accent-blue: #2563eb;
35
+ --accent-blue-hover: #1d4ed8;
36
+ --accent-blue-subtle: rgba(37, 99, 235, 0.08);
37
+ --accent-emerald: #059669;
38
+ --accent-amber: #d97706;
39
+ --accent-red: #dc2626;
40
+ --accent-purple: #7c3aed;
41
+
42
+ --border-subtle: rgba(0, 0, 0, 0.08);
43
+ --border-active: rgba(37, 99, 235, 0.4);
44
+
45
+ --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.03);
46
+ --shadow-card-hover: 0 2px 8px rgba(0, 0, 0, 0.06);
47
+ --shadow-glow: none;
48
+
49
+ --grad: linear-gradient(135deg, #2563eb 0%, #0284c7 100%);
50
+ --grad-hero: linear-gradient(135deg, rgba(37, 99, 235, 0.05) 0%, rgba(2, 132, 199, 0.05) 100%);
51
+ --hero-text: #1e293b;
52
+ --hero-desc: #475569;
53
+ }
54
+
55
+ [data-theme='dark'] {
56
+ /* Dark Theme */
57
+ --bg-primary: #0f1117;
58
+ --bg-secondary: #1a1d27;
59
+ --bg-card: #1e2130;
60
+ --bg-glass: rgba(255, 255, 255, 0.04);
61
+ --bg-hover: rgba(255, 255, 255, 0.05);
62
+
63
+ --text-primary: #e8eaed;
64
+ --text-secondary: #9aa0b0;
65
+ --text-muted: #5f6577;
66
+
67
+ --accent-blue: #3b82f6;
68
+ --accent-blue-hover: #2563eb;
69
+ --accent-blue-subtle: rgba(59, 130, 246, 0.12);
70
+ --accent-emerald: #10b981;
71
+ --accent-amber: #f59e0b;
72
+ --accent-red: #ef4444;
73
+ --accent-purple: #8b5cf6;
74
+
75
+ --border-subtle: rgba(255, 255, 255, 0.07);
76
+ --border-active: rgba(59, 130, 246, 0.5);
77
+
78
+ --shadow-card: 0 1px 4px rgba(0, 0, 0, 0.2);
79
+ --shadow-card-hover: 0 4px 16px rgba(0, 0, 0, 0.3);
80
+ --shadow-glow: 0 0 20px rgba(59, 130, 246, 0.1);
81
+
82
+ --grad: linear-gradient(135deg, #3b82f6 0%, #0ea5e9 100%);
83
+ --grad-hero: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(14, 165, 233, 0.08) 100%);
84
+ --hero-text: #e8eaed;
85
+ --hero-desc: #9aa0b0;
86
+ }
87
+
88
+ body {
89
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
90
+ background-color: var(--bg-primary);
91
+ color: var(--text-primary);
92
+ line-height: 1.6;
93
+ display: flex;
94
+ min-height: 100vh;
95
+ }
96
+
97
+ ::selection {
98
+ background: var(--accent-blue-subtle);
99
+ color: var(--accent-blue);
100
+ }
101
+
102
+ a {
103
+ color: var(--accent-blue);
104
+ text-decoration: none;
105
+ font-weight: 500;
106
+ }
107
+ a:hover {
108
+ text-decoration: underline;
109
+ }
110
+
111
+ /* Sidebar Styling */
112
+ .sidebar {
113
+ position: fixed;
114
+ top: 0;
115
+ left: 0;
116
+ width: 260px;
117
+ height: 100vh;
118
+ background: var(--bg-secondary);
119
+ border-right: 1px solid var(--border-subtle);
120
+ padding: 24px 0;
121
+ overflow-y: auto;
122
+ z-index: 100;
123
+ }
124
+
125
+ .sidebar-logo {
126
+ padding: 0 24px 20px;
127
+ border-bottom: 1px solid var(--border-subtle);
128
+ margin-bottom: 16px;
129
+ }
130
+
131
+ .sidebar-logo h1 {
132
+ font-size: 16px;
133
+ font-weight: 700;
134
+ color: var(--accent-blue);
135
+ letter-spacing: -0.3px;
136
+ font-family: 'Outfit', sans-serif;
137
+ }
138
+
139
+ .sidebar-logo p {
140
+ font-size: 11px;
141
+ color: var(--text-muted);
142
+ margin-top: 4px;
143
+ }
144
+
145
+ .sidebar nav a {
146
+ display: flex;
147
+ align-items: center;
148
+ padding: 10px 24px;
149
+ font-size: 13px;
150
+ color: var(--text-secondary);
151
+ transition: all 0.2s;
152
+ border-left: 3px solid transparent;
153
+ text-decoration: none;
154
+ }
155
+
156
+ .sidebar nav a:hover, .sidebar nav a.active {
157
+ color: var(--accent-blue);
158
+ background: var(--accent-blue-subtle);
159
+ border-left-color: var(--accent-blue);
160
+ }
161
+
162
+ .sidebar nav a .num {
163
+ display: inline-flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ width: 20px;
167
+ height: 20px;
168
+ border-radius: 4px;
169
+ background: var(--bg-glass);
170
+ font-size: 10px;
171
+ font-weight: 600;
172
+ margin-right: 10px;
173
+ color: var(--text-muted);
174
+ }
175
+
176
+ .sidebar nav a.active .num {
177
+ background: var(--accent-blue);
178
+ color: white;
179
+ }
180
+
181
+ /* Main Area */
182
+ .main {
183
+ margin-left: 260px;
184
+ flex: 1;
185
+ padding: 0;
186
+ overflow-y: auto;
187
+ height: 100vh;
188
+ }
189
+
190
+ /* Embedded in Iframe */
191
+ body.in-iframe .sidebar {
192
+ display: none !important;
193
+ }
194
+
195
+ body.in-iframe .main {
196
+ margin-left: 0 !important;
197
+ width: 100% !important;
198
+ height: 100% !important;
199
+ }
200
+
201
+ body.in-iframe .content {
202
+ padding: 32px 24px 60px;
203
+ max-width: 100%;
204
+ }
205
+
206
+ /* Hero Header */
207
+ .hero {
208
+ background: var(--grad-hero);
209
+ padding: 48px 40px;
210
+ border-bottom: 1px solid var(--border-subtle);
211
+ position: relative;
212
+ overflow: hidden;
213
+ }
214
+
215
+ .hero h1 {
216
+ font-size: 26px;
217
+ font-weight: 700;
218
+ color: var(--hero-text);
219
+ font-family: 'Outfit', sans-serif;
220
+ letter-spacing: -0.5px;
221
+ }
222
+
223
+ .hero p {
224
+ font-size: 13.5px;
225
+ color: var(--hero-desc);
226
+ margin-top: 8px;
227
+ max-width: 800px;
228
+ }
229
+
230
+ .hero .badge {
231
+ display: inline-block;
232
+ padding: 4px 12px;
233
+ background: var(--bg-glass);
234
+ border: 1px solid var(--border-subtle);
235
+ border-radius: 20px;
236
+ font-size: 11px;
237
+ font-weight: 600;
238
+ color: var(--text-secondary);
239
+ margin-top: 12px;
240
+ }
241
+
242
+ /* Content Container */
243
+ .content {
244
+ padding: 48px;
245
+ max-width: 1000px;
246
+ margin: 0 auto;
247
+ }
248
+
249
+ .section {
250
+ margin-bottom: 48px;
251
+ scroll-margin-top: 40px;
252
+ }
253
+
254
+ h2 {
255
+ font-size: 18px;
256
+ font-weight: 700;
257
+ margin-bottom: 16px;
258
+ position: relative;
259
+ padding-left: 12px;
260
+ color: var(--text-primary);
261
+ font-family: 'Outfit', sans-serif;
262
+ }
263
+
264
+ h2::before {
265
+ content: '';
266
+ position: absolute;
267
+ left: 0;
268
+ top: 3px;
269
+ bottom: 3px;
270
+ width: 3px;
271
+ border-radius: 1.5px;
272
+ background: var(--accent-blue);
273
+ }
274
+
275
+ h3 {
276
+ font-size: 14px;
277
+ font-weight: 600;
278
+ margin: 24px 0 12px;
279
+ color: var(--accent-blue);
280
+ }
281
+
282
+ h4 {
283
+ font-size: 13px;
284
+ font-weight: 600;
285
+ margin: 16px 0 8px;
286
+ color: var(--text-primary);
287
+ }
288
+
289
+ p, li {
290
+ margin-bottom: 8px;
291
+ color: var(--text-secondary);
292
+ font-size: 13px;
293
+ line-height: 1.6;
294
+ }
295
+
296
+ ul, ol {
297
+ margin: 0 0 16px 20px;
298
+ }
299
+
300
+ /* Cards & Layout */
301
+ .card {
302
+ background: var(--bg-secondary);
303
+ border: 1px solid var(--border-subtle);
304
+ border-radius: var(--radius-lg, 12px);
305
+ padding: 20px;
306
+ margin: 16px 0;
307
+ box-shadow: var(--shadow-card);
308
+ }
309
+
310
+ .card.tip, .card.info {
311
+ border-color: rgba(37, 99, 235, 0.2);
312
+ background: var(--accent-blue-subtle);
313
+ }
314
+
315
+ .card.tip h4, .card.info h4 {
316
+ color: var(--accent-blue);
317
+ margin-top: 0;
318
+ }
319
+
320
+ .card.warn {
321
+ border-color: rgba(220, 38, 38, 0.2);
322
+ background: rgba(220, 38, 38, 0.03);
323
+ }
324
+
325
+ .card.warn h4 {
326
+ color: var(--accent-red);
327
+ margin-top: 0;
328
+ }
329
+
330
+ /* Table */
331
+ table {
332
+ width: 100%;
333
+ border-collapse: collapse;
334
+ margin: 16px 0;
335
+ border-radius: 8px;
336
+ overflow: hidden;
337
+ border: 1px solid var(--border-subtle);
338
+ }
339
+
340
+ th {
341
+ background: var(--bg-glass);
342
+ padding: 10px 14px;
343
+ text-align: left;
344
+ font-size: 12px;
345
+ font-weight: 600;
346
+ color: var(--text-primary);
347
+ border-bottom: 1px solid var(--border-subtle);
348
+ }
349
+
350
+ td {
351
+ padding: 10px 14px;
352
+ font-size: 12px;
353
+ border-top: 1px solid var(--border-subtle);
354
+ color: var(--text-secondary);
355
+ background: var(--bg-secondary);
356
+ }
357
+
358
+ tr:hover td {
359
+ background: var(--bg-hover);
360
+ }
361
+
362
+ .role-card {
363
+ display: grid;
364
+ grid-template-columns: repeat(3, 1fr);
365
+ gap: 16px;
366
+ margin: 24px 0;
367
+ }
368
+
369
+ .role {
370
+ background: var(--bg-secondary);
371
+ border: 1px solid var(--border-subtle);
372
+ border-radius: 12px;
373
+ padding: 18px;
374
+ text-align: center;
375
+ box-shadow: var(--shadow-card);
376
+ }
377
+
378
+ .role:hover {
379
+ border-color: var(--accent-blue);
380
+ }
381
+
382
+ .role .icon {
383
+ font-size: 24px;
384
+ margin-bottom: 8px;
385
+ }
386
+
387
+ .role .name {
388
+ font-size: 13.5px;
389
+ font-weight: 700;
390
+ color: var(--text-primary);
391
+ margin-bottom: 6px;
392
+ }
393
+
394
+ .role .desc {
395
+ font-size: 11.5px;
396
+ color: var(--text-secondary);
397
+ line-height: 1.4;
398
+ }
399
+
400
+ .flow {
401
+ display: flex;
402
+ align-items: center;
403
+ gap: 6px;
404
+ margin: 16px 0;
405
+ flex-wrap: wrap;
406
+ }
407
+
408
+ .flow-step {
409
+ background: var(--bg-secondary);
410
+ border: 1px solid var(--border-subtle);
411
+ border-radius: 8px;
412
+ padding: 8px 12px;
413
+ font-size: 11.5px;
414
+ color: var(--text-primary);
415
+ font-weight: 500;
416
+ box-shadow: var(--shadow-card);
417
+ }
418
+
419
+ .flow-arrow {
420
+ color: var(--accent-blue);
421
+ font-size: 13px;
422
+ font-weight: bold;
423
+ }
424
+
425
+ /* Code */
426
+ code {
427
+ font-family: 'JetBrains Mono', 'SF Mono', monospace;
428
+ font-size: 11px;
429
+ background: var(--accent-blue-subtle);
430
+ padding: 2px 5px;
431
+ border-radius: 4px;
432
+ color: var(--accent-blue);
433
+ }
434
+
435
+ .code-wrap {
436
+ position: relative;
437
+ margin: 16px 0;
438
+ border-radius: 8px;
439
+ overflow: hidden;
440
+ border: 1px solid var(--border-subtle);
441
+ box-shadow: var(--shadow-card);
442
+ }
443
+
444
+ .code-header {
445
+ display: flex;
446
+ align-items: center;
447
+ justify-content: space-between;
448
+ padding: 6px 14px;
449
+ background: var(--bg-hover);
450
+ border-bottom: 1px solid var(--border-subtle);
451
+ }
452
+
453
+ .code-header span {
454
+ font-size: 11px;
455
+ color: var(--text-muted);
456
+ font-family: 'JetBrains Mono', monospace;
457
+ }
458
+
459
+ .copy-btn {
460
+ padding: 2px 8px;
461
+ border: 1px solid var(--border-subtle);
462
+ border-radius: 4px;
463
+ background: var(--bg-secondary);
464
+ color: var(--text-secondary);
465
+ cursor: pointer;
466
+ font-size: 10px;
467
+ transition: all 0.15s;
468
+ }
469
+
470
+ .copy-btn:hover {
471
+ background: var(--accent-blue);
472
+ color: white;
473
+ border-color: var(--accent-blue);
474
+ }
475
+
476
+ pre {
477
+ background: var(--bg-card);
478
+ padding: 12px 16px;
479
+ overflow-x: auto;
480
+ font-family: 'JetBrains Mono', monospace;
481
+ font-size: 11.5px;
482
+ line-height: 1.5;
483
+ color: var(--text-secondary);
484
+ }
485
+
486
+ pre .comment { color: var(--text-muted); font-style: italic; }
487
+ pre .keyword { color: var(--accent-purple); font-weight: 600; }
488
+ pre .string { color: var(--accent-emerald); }
489
+ pre .cmd { color: var(--accent-blue); }
490
+
491
+ .menu-path {
492
+ color: var(--accent-blue);
493
+ font-weight: 600;
494
+ }
495
+
496
+ /* Installation Specifics */
497
+ .req-grid {
498
+ display: grid;
499
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
500
+ gap: 16px;
501
+ margin: 16px 0;
502
+ }
503
+
504
+ .req-item {
505
+ background: var(--bg-secondary);
506
+ border: 1px solid var(--border-subtle);
507
+ border-radius: 12px;
508
+ padding: 16px;
509
+ text-align: center;
510
+ box-shadow: var(--shadow-card);
511
+ }
512
+
513
+ .req-item .icon {
514
+ font-size: 24px;
515
+ margin-bottom: 6px;
516
+ }
517
+
518
+ .req-item .label {
519
+ font-size: 10px;
520
+ color: var(--text-muted);
521
+ text-transform: uppercase;
522
+ letter-spacing: 0.5px;
523
+ }
524
+
525
+ .req-item .value {
526
+ font-size: 13.5px;
527
+ font-weight: 600;
528
+ color: var(--text-primary);
529
+ margin-top: 4px;
530
+ }
531
+
532
+ .step {
533
+ display: flex;
534
+ gap: 16px;
535
+ margin: 16px 0;
536
+ padding: 16px;
537
+ background: var(--bg-secondary);
538
+ border: 1px solid var(--border-subtle);
539
+ border-radius: 12px;
540
+ box-shadow: var(--shadow-card);
541
+ }
542
+
543
+ .step-num {
544
+ flex-shrink: 0;
545
+ width: 28px;
546
+ height: 28px;
547
+ border-radius: 6px;
548
+ background: var(--grad);
549
+ display: flex;
550
+ align-items: center;
551
+ justify-content: center;
552
+ font-weight: 700;
553
+ font-size: 12px;
554
+ color: #fff;
555
+ }
556
+
557
+ .step-body h4 {
558
+ margin: 0 0 4px;
559
+ color: var(--text-primary);
560
+ }
561
+
562
+ .step-body p {
563
+ margin: 0;
564
+ color: var(--text-secondary);
565
+ font-size: 12.5px;
566
+ }
567
+
568
+ .port-badge {
569
+ display: inline-block;
570
+ padding: 2px 6px;
571
+ border-radius: 4px;
572
+ background: var(--accent-blue-subtle);
573
+ color: var(--accent-blue);
574
+ font-family: 'JetBrains Mono', monospace;
575
+ font-size: 11px;
576
+ font-weight: 600;
577
+ }
578
+
579
+ .top-toolbar {
580
+ position: absolute;
581
+ top: 20px;
582
+ right: 20px;
583
+ z-index: 10;
584
+ display: flex;
585
+ gap: 8px;
586
+ }
587
+
588
+ .lang-selector {
589
+ background: var(--bg-secondary);
590
+ border: 1px solid var(--border-subtle);
591
+ padding: 4px 10px;
592
+ border-radius: 6px;
593
+ color: var(--text-secondary);
594
+ font-size: 11.5px;
595
+ outline: none;
596
+ cursor: pointer;
597
+ box-shadow: var(--shadow-card);
598
+ }
599
+
600
+ .lang-selector:hover {
601
+ border-color: var(--accent-blue);
602
+ color: var(--text-primary);
603
+ }
604
+
605
+ @media (max-width: 900px) {
606
+ .sidebar { display: none; }
607
+ .main { margin-left: 0; }
608
+ .content { padding: 24px; }
609
+ .hero { padding: 32px 24px; }
610
+ .hero h1 { font-size: 22px; }
611
+ }
612
+
613
+ @media print {
614
+ .sidebar { display: none; }
615
+ .main { margin-left: 0; }
616
+ }
617
+ </style>
618
+ </head>
619
+ <body>
620
+
621
+ <aside class="sidebar">
622
+ <div class="sidebar-logo">
623
+ <h1>๐Ÿ“‹ CertBridge</h1>
624
+ <p data-i18n="sidebar.ver">์„ค์น˜ ๊ฐ€์ด๋“œ v0.1.0</p>
625
+ </div>
626
+ <nav id="nav">
627
+ <a href="#requirements" class="active"><span class="num">1</span><span data-i18n="nav.req">์‹œ์Šคํ…œ ์š”๊ตฌ์‚ฌํ•ญ</span></a>
628
+ <a href="#server-install"><span class="num">2</span><span data-i18n="nav.server">์„œ๋ฒ„ ์„ค์น˜ (Ubuntu)</span></a>
629
+ <a href="#client-install"><span class="num">3</span><span data-i18n="nav.client">ํด๋ผ์ด์–ธํŠธ ์„ค์น˜ (Windows)</span></a>
630
+ <a href="#network"><span class="num">4</span><span data-i18n="nav.network">๋„คํŠธ์›Œํฌ ๊ตฌ์„ฑ</span></a>
631
+ <a href="#initial-setup"><span class="num">5</span><span data-i18n="nav.setup">์ดˆ๊ธฐ ์„ค์ •</span></a>
632
+ <a href="#troubleshooting"><span class="num">6</span><span data-i18n="nav.trouble">๋ฌธ์ œ ํ•ด๊ฒฐ</span></a>
633
+ </nav>
634
+ </aside>
635
+
636
+ <div class="main">
637
+ <div class="top-toolbar">
638
+ <select id="lang-select" class="lang-selector" onchange="changeLanguage(this.value)">
639
+ <option value="ko">ํ•œ๊ตญ์–ด</option>
640
+ <option value="en">English</option>
641
+ <option value="zh">ไธญๆ–‡</option>
642
+ <option value="ja">ๆ—ฅๆœฌ่ชž</option>
643
+ <option value="de">Deutsch</option>
644
+ <option value="fr">Franรงais</option>
645
+ <option value="es">Espaรฑol</option>
646
+ <option value="pt">Portuguรชs</option>
647
+ <option value="hu">Magyar</option>
648
+ </select>
649
+ </div>
650
+
651
+ <div class="hero">
652
+ <h1 data-i18n="hero.title">CertBridge ์„ค์น˜ ๋ฐ ์…‹์—… ๊ฐ€์ด๋“œ</h1>
653
+ <p data-i18n="hero.desc">AI ์„ฑ์ ์„œ ์ž๋™ ํŒŒ์‹ฑ ๋ฐ ๋ ˆ๊ฑฐ์‹œ ํ†ตํ•ฉ ์‹œ์Šคํ…œ์„ Ubuntu ์„œ๋ฒ„์™€ Windows ํด๋ผ์ด์–ธํŠธ์— ๊ตฌ์ถ•ํ•˜๋Š” ์ƒ์„ธ ํ”„๋กœํ† ์ฝœ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.</p>
654
+ <span class="badge" data-i18n="hero.badge">๐Ÿ“ฆ v0.1.0 ยท Ubuntu 22.04+ ยท Windows 10+</span>
655
+ </div>
656
+
657
+ <div class="content">
658
+
659
+ <!-- 1. Requirements -->
660
+ <section class="section" id="requirements">
661
+ <h2 data-i18n="req.title">1. ์‹œ์Šคํ…œ ์š”๊ตฌ์‚ฌํ•ญ</h2>
662
+
663
+ <h3 data-i18n="req.server_title">๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ ํ•˜๋“œ์›จ์–ด ๊ถŒ์žฅ์‚ฌ์–‘ (Ubuntu OS)</h3>
664
+ <div class="req-grid">
665
+ <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>
666
+ <div class="req-item"><div class="icon">๐Ÿง </div><div class="label" data-i18n="req.ram">RAM</div><div class="value">4GB+ (8GB ๊ถŒ์žฅ)</div></div>
667
+ <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>
668
+ <div class="req-item"><div class="icon">๐ŸŒ</div><div class="label" data-i18n="req.net">Network</div><div class="value">์ •์  IP / ์™ธ๋ถ€ ์•„์›ƒ๋ฐ”์šด๋“œ</div></div>
669
+ </div>
670
+
671
+ <h3 data-i18n="req.client_title">๐Ÿ’ป ํด๋ผ์ด์–ธํŠธ ์‹คํ–‰ ํ™˜๊ฒฝ (Windows)</h3>
672
+ <div class="req-grid">
673
+ <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>
674
+ <div class="req-item"><div class="icon">๐Ÿง </div><div class="label" data-i18n="req.ram">RAM</div><div class="value">2GB+ (4GB ๊ถŒ์žฅ)</div></div>
675
+ <div class="req-item"><div class="icon">๐ŸŒ</div><div class="label" data-i18n="req.net">Network</div><div class="value">์ค‘์•™ ์„œ๋ฒ„ 8090 ํฌํŠธ ์ ‘๊ทผ</div></div>
676
+ </div>
677
+
678
+ <h3 data-i18n="req.software">๐Ÿ”ง ํ•„์ˆ˜ ์ธํ”„๋ผ ์†Œํ”„ํŠธ์›จ์–ด (์„œ๋ฒ„ ์ธก)</h3>
679
+ <table>
680
+ <thead>
681
+ <tr>
682
+ <th data-i18n="req.tb_soft">์†Œํ”„ํŠธ์›จ์–ด</th>
683
+ <th data-i18n="req.tb_ver">์ตœ์†Œ ๋ฒ„์ „</th>
684
+ <th data-i18n="req.tb_usage">์šฉ๋„</th>
685
+ </tr>
686
+ </thead>
687
+ <tbody>
688
+ <tr>
689
+ <td>Docker Engine</td>
690
+ <td>24.0.0+</td>
691
+ <td data-i18n="req.tb_docker_desc">์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์บ์‹œ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜</td>
692
+ </tr>
693
+ <tr>
694
+ <td>Docker Compose</td>
695
+ <td>v2.20.0+</td>
696
+ <td data-i18n="req.tb_compose_desc">๋ฉ€ํ‹ฐ ์ปจํ…Œ์ด๋„ˆ ์ธํ”„๋ผ ์Šคํƒ ํ†ตํ•ฉ ์‹คํ–‰</td>
697
+ </tr>
698
+ <tr>
699
+ <td>Node.js Runtime</td>
700
+ <td>20.x LTS+</td>
701
+ <td data-i18n="req.tb_node_desc">CertBridge Express API ์ฝ”์–ด ์„œ๋ฒ„ ๊ตฌ๋™</td>
702
+ </tr>
703
+ </tbody>
704
+ </table>
705
+ </section>
706
+
707
+ <!-- 2. Server Install -->
708
+ <section class="section" id="server-install">
709
+ <h2 data-i18n="server.title">2. ์„œ๋ฒ„ ์„ค์น˜ (Ubuntu)</h2>
710
+
711
+ <h3 data-i18n="server.auto">2-1. ์ž๋™ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์„ค์น˜ (๊ถŒ์žฅ)</h3>
712
+ <p data-i18n="server.auto_desc">์ œ๊ณต๋œ setup.sh ํŒŒ์ผ์„ ํ™œ์šฉํ•ด ๋„์ปค ์—”์ง„, ํ™˜๊ฒฝ๋ณ€์ˆ˜, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, systemd ์„œ๋น„์Šค ๋“ฑ๋ก์„ ํ•œ ๋ฒˆ์— ์ž๋™์œผ๋กœ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.</p>
713
+
714
+ <div class="code-wrap">
715
+ <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
716
+ <pre><span class="comment"># 1. ์„œ๋ฒ„ ํŒจํ‚ค์ง€ ์•„์นด์ด๋ธŒ ๋‹ค์šด๋กœ๋“œ</span>
717
+ <span class="cmd">wget</span> https://huggingface.co/VanyaJ/certbridge/resolve/main/certbridge-server-v0.1.0-ubuntu.tar.gz
718
+
719
+ <span class="comment"># 2. ํŒŒ์ผ ์••์ถ• ํ•ด์ œ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ ์ด๋™</span>
720
+ <span class="cmd">tar</span> -xzf certbridge-server-v0.1.0-ubuntu.tar.gz
721
+ <span class="cmd">cd</span> certbridge-server-ubuntu
722
+
723
+ <span class="comment"># 3. ์„ค์น˜ ๊ถŒํ•œ ๋ถ€์—ฌ ๋ฐ ์‰˜ ์‹คํ–‰</span>
724
+ <span class="cmd">chmod</span> +x setup.sh
725
+ <span class="cmd">sudo</span> ./setup.sh</pre>
726
+ </div>
727
+
728
+ <h3 data-i18n="server.manual">2-2. ๋‹จ๊ณ„๋ณ„ ์ˆ˜๋™ ์„ค์น˜ ํ”„๋กœํ† ์ฝœ</h3>
729
+
730
+ <h4 data-i18n="server.d_setup">Docker ์„ค์น˜ ๋ฐ ์‹คํ–‰ ํ™˜๊ฒฝ ๊ตฌ์„ฑ</h4>
731
+ <div class="code-wrap">
732
+ <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
733
+ <pre><span class="comment"># ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธ ๋ฐ GPG ํ‚ค ์ถ”๊ฐ€</span>
734
+ <span class="cmd">sudo apt-get</span> update
735
+ <span class="cmd">sudo apt-get install -y</span> ca-certificates curl gnupg
736
+ <span class="cmd">sudo install -m 0755 -d</span> /etc/apt/keyrings
737
+ <span class="cmd">curl -fsSL</span> https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
738
+
739
+ <span class="comment"># ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ •๋ณด ์ถ”๊ฐ€ ๋ฐ ๋„์ปค ์ฝ”์–ด ํŒจํ‚ค์ง€ ์„ค์น˜</span>
740
+ <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
741
+ <span class="cmd">sudo apt-get</span> update
742
+ <span class="cmd">sudo apt-get install -y</span> docker-ce docker-ce-cli containerd.io docker-compose-plugin</pre>
743
+ </div>
744
+
745
+ <h4 data-i18n="server.compose">๋„์ปค ์ปดํฌ์ฆˆ ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ๊ตฌ์„ฑ์š”์†Œ ์‹คํ–‰</h4>
746
+ <p data-i18n="server.compose_desc">PostgreSQL, Redis, MinIO ์˜ค๋ธŒ์ ํŠธ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ Neo4j ๊ทธ๋ž˜ํ”„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ•˜๋‚˜์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šคํƒ์œผ๋กœ ๋„์›๋‹ˆ๋‹ค.</p>
747
+ <div class="code-wrap">
748
+ <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
749
+ <pre><span class="comment"># server ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€ docker-compose ํŒŒ์ผ ์‹คํ–‰</span>
750
+ <span class="cmd">sudo docker compose</span> up -d
751
+
752
+ <span class="comment"># ๊ธฐ๋™ ์ƒํƒœ ์ •์ƒํ™” ํ™•์ธ</span>
753
+ <span class="cmd">sudo docker compose</span> ps</pre>
754
+ </div>
755
+
756
+ <h4 data-i18n="server.env_setup">API ํ™˜๊ฒฝ ํŒŒ์ผ ๊ตฌ์„ฑ (.env)</h4>
757
+ <div class="code-wrap">
758
+ <div class="code-header"><span>.env</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
759
+ <pre># Central Data Store Connections
760
+ DATABASE_URL=postgres://n8n:n8n_password@localhost:5432/certbridge
761
+ REDIS_URL=redis://localhost:6379/2
762
+
763
+ # File Storage Configuration
764
+ MINIO_ENDPOINT=localhost:9000
765
+ MINIO_ACCESS_KEY=minioadmin
766
+ MINIO_SECRET_KEY=minioadmin
767
+ MINIO_BUCKET=certbridge
768
+
769
+ # Neo4j Graph DB Config
770
+ NEO4J_URI=bolt://localhost:7687
771
+ NEO4J_USER=neo4j
772
+ NEO4J_PASSWORD=certbridge123
773
+
774
+ # Core Express API Config
775
+ API_PORT=8090
776
+ JWT_SECRET=your-premium-secret-key-change-in-production</pre>
777
+ </div>
778
+
779
+ <div class="card warn">
780
+ <h4 data-i18n="server.warn_title">โš ๏ธ ํ•„์ˆ˜ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ</h4>
781
+ <p data-i18n="server.warn_desc">์šด์˜ ํ™˜๊ฒฝ ๋ฐฐํฌ ์‹œ, <code>JWT_SECRET</code> ์•”ํ˜ธํ™” ํ‚ค์™€ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒจ์Šค์›Œ๋“œ ๋ฐ MinIO ACCESS/SECRET ํ‚ค๋ฅผ ๋‚œ์ˆ˜ ํ˜•ํƒœ๋กœ ๋ฐ˜๋“œ์‹œ ๊ต์ฒดํ•ด ์ ์šฉํ•˜์‹ญ์‹œ์˜ค.</p>
782
+ </div>
783
+
784
+ <h4 data-i18n="server.api_start">API ์„œ๋ฒ„ ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ๊ธฐ๋™</h4>
785
+ <div class="code-wrap">
786
+ <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
787
+ <pre><span class="comment"># server/api ํด๋”๋กœ ์ด๋™ ํ›„ ๋ชจ๋“ˆ ๋‹ค์šด๋กœ๋“œ</span>
788
+ <span class="cmd">npm</span> install --production
789
+
790
+ <span class="comment"># API ์„œ๋ฒ„ ๋ฐ๋ชฌ ๋ชจ๋“œ๋กœ ์ˆ˜๋™ ๊ธฐ๋™</span>
791
+ <span class="cmd">source</span> .env && <span class="cmd">node</span> server.js
792
+
793
+ <span class="comment"># ํ—ฌ์Šค์ฒดํฌ ์ •์ƒ ์ˆ˜์‹  ํ™•์ธ</span>
794
+ <span class="cmd">curl</span> http://localhost:8090/health</pre>
795
+ </div>
796
+ </section>
797
+
798
+ <!-- 3. Client Install -->
799
+ <section class="section" id="client-install">
800
+ <h2 data-i18n="client.title">3. ํด๋ผ์ด์–ธํŠธ ์„ค์น˜ (Windows)</h2>
801
+
802
+ <div class="step">
803
+ <div class="step-num">1</div>
804
+ <div class="step-body">
805
+ <h4 data-i18n="client.s1_title">์„ค์น˜ ํŒŒ์ผ ํŒจํ‚ค์ง€ ๋‹ค์šด๋กœ๋“œ</h4>
806
+ <p data-i18n="client.s1_desc">๊ณต์‹ ๋ฆด๋ฆฌ์ฆˆ ๋ณด๊ด€์†Œ(HuggingFace ๋˜๋Š” ํšŒ์‚ฌ CDN๋ง)์—์„œ <code>CertBridge_0.1.0_x64-setup.exe</code>(์ž๋™ ๋นŒ๋“œ ๋ณธ)๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.</p>
807
+ </div>
808
+ </div>
809
+
810
+ <div class="step">
811
+ <div class="step-num">2</div>
812
+ <div class="step-body">
813
+ <h4 data-i18n="client.s2_title">์„ค์น˜ ๋งˆ๋ฒ•์‚ฌ ์ง„ํ–‰ ๋ฐ ์–ธ์–ด ํƒ€๊ฒŸํŒ…</h4>
814
+ <p data-i18n="client.s2_desc">์„ค์น˜ ๋งˆ๋ฒ•์‚ฌ ๋‹จ๊ณ„์—์„œ ์›ํ•˜๋Š” ์–ธ์–ด๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ค์ •(ํ•œ๊ธ€/์˜์–ด ์ž๋™ ๋งค์นญ)ํ•œ ํ›„, ์ง€์ •๋œ ๊ฒฝ๋กœ์— ํด๋ผ์ด์–ธํŠธ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋นŒ๋“œ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.</p>
815
+ </div>
816
+ </div>
817
+
818
+ <div class="step">
819
+ <div class="step-num">3</div>
820
+ <div class="step-body">
821
+ <h4 data-i18n="client.s3_title">์ค‘์•™ ์—”๋“œํฌ์ธํŠธ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ</h4>
822
+ <p data-i18n="client.s3_desc">๋ฐ”ํƒ•ํ™”๋ฉด ๋‹จ์ถ• ์•„์ด์ฝ˜์„ ํ†ตํ•ด ์‹คํ–‰ ํ›„, ์šฐ์ธก ์•„๋ž˜์˜ <strong>์„ค์ • ์•„์ด์ฝ˜(โš™๏ธ)</strong>์„ ํด๋ฆญํ•ด ์ค‘์•™์— ๊ฐœ๋ฐฉ๋œ API ์„œ๋ฒ„ IP ๋ฐ ๋„๋ฉ”์ธ์„ ์ž…๋ ฅํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค: <code>http://&lt;์ค‘์•™์„œ๋ฒ„_IP&gt;:8090</code></p>
823
+ </div>
824
+ </div>
825
+ </section>
826
+
827
+ <!-- 4. Network -->
828
+ <section class="section" id="network">
829
+ <h2 data-i18n="net.title">4. ๋„คํŠธ์›Œํฌ ๋ฐ ํฌํŠธ ๊ตฌ์„ฑ</h2>
830
+ <h3 data-i18n="net.ports">์ธํ”„๋ผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ ํฌํŠธ ํ˜„ํ™ฉ</h3>
831
+ <table>
832
+ <thead>
833
+ <tr>
834
+ <th data-i18n="net.tb_serv">์—ฐ๋™ ์„œ๋น„์Šค</th>
835
+ <th data-i18n="net.tb_port">๊ธฐ๋ณธ ํฌํŠธ</th>
836
+ <th data-i18n="net.tb_proto">ํ”„๋กœํ† ์ฝœ</th>
837
+ <th data-i18n="net.tb_desc">์„ค๋ช…</th>
838
+ </tr>
839
+ </thead>
840
+ <tbody>
841
+ <tr>
842
+ <td>CertBridge API Gateway</td>
843
+ <td><span class="port-badge">8090</span></td>
844
+ <td>HTTP</td>
845
+ <td data-i18n="net.tb_p1">์ฝ”์–ด ํด๋ผ์ด์–ธํŠธ REST & ํ•˜ํŠธ๋น„ํŠธ ์—ฐ๊ฒฐ ์ฐฝ๊ตฌ (์ธ๋ฐ”์šด๋“œ ํ—ˆ์šฉ ํ•„์ˆ˜)</td>
846
+ </tr>
847
+ <tr>
848
+ <td>Neo4j Bolt Driver</td>
849
+ <td><span class="port-badge">7687</span></td>
850
+ <td>TCP / Bolt</td>
851
+ <td data-i18n="net.tb_p2">ํ’ˆ์งˆ ์ง€์‹ ์˜จํ†จ๋กœ์ง€ ๊ณ ์† ์ปค๋„ฅํ„ฐ ํฌํŠธ</td>
852
+ </tr>
853
+ <tr>
854
+ <td>MinIO Storage API</td>
855
+ <td><span class="port-badge">9000</span></td>
856
+ <td>HTTP</td>
857
+ <td data-i18n="net.tb_p3">์„ฑ์ ์„œ PDF ๋ฌผ๋ฆฌ ์›๋ณธ ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์Šคํ† ๋ฆฌ์ง€ API</td>
858
+ </tr>
859
+ <tr>
860
+ <td>n8n Automation Engine</td>
861
+ <td><span class="port-badge">5678</span></td>
862
+ <td>HTTP</td>
863
+ <td data-i18n="net.tb_p4">์›Œํฌํ”Œ๋กœ์šฐ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ ํŠธ๋ฆฌ๊ฑฐ ์—”๋“œํฌ์ธํŠธ</td>
864
+ </tr>
865
+ </tbody>
866
+ </table>
867
+
868
+ <h3 data-i18n="net.firewall">Ubuntu ๋ฐฉํ™”๋ฒฝ (UFW) ์„ค์ • ์Šคํฌ๋ฆฝํŠธ</h3>
869
+ <div class="code-wrap">
870
+ <div class="code-header"><span>bash</span><button class="copy-btn" onclick="copyCode(this)">๋ณต์‚ฌ</button></div>
871
+ <pre><span class="comment"># ๊ธฐ๋ณธ ํฌํŠธ ๊ฐœ๋ฐฉ</span>
872
+ <span class="cmd">sudo ufw</span> allow 8090/tcp
873
+
874
+ <span class="comment"># ๋ณด์•ˆ ๊ถŒ๊ณ : ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์ž IP ๋Œ€์—ญ๋งŒ Neo4j, MinIO ์ฝ˜์†” ํ—ˆ์šฉ</span>
875
+ <span class="cmd">sudo ufw</span> allow from 192.168.10.0/24 to any port 7474
876
+ <span class="cmd">sudo ufw</span> allow from 192.168.10.0/24 to any port 9001</pre>
877
+ </div>
878
+ </section>
879
+
880
+ <!-- 5. Initial Setup -->
881
+ <section class="section" id="initial-setup">
882
+ <h2 data-i18n="setup.title">5. ์ค‘์•™ ๊ด€๋ฆฌ ํ™˜๊ฒฝ ์ดˆ๊ธฐํ™”</h2>
883
+ <h3 data-i18n="setup.admin">5-1. ์ตœ์ดˆ ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ •๋ณด</h3>
884
+ <p data-i18n="setup.admin_desc">์„ค์น˜๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋งˆ์ณ์ง€๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ๋“œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์•„๋ž˜ ๊ด€๋ฆฌ์ž ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:</p>
885
+ <table>
886
+ <thead>
887
+ <tr>
888
+ <th data-i18n="setup.tb_col">์„ค์ • ์ •๋ณด</th>
889
+ <th data-i18n="setup.tb_val">์ง€์ •๋œ ๊ธฐ๋ณธ ๊ฐ’</th>
890
+ </tr>
891
+ </thead>
892
+ <tbody>
893
+ <tr>
894
+ <td data-i18n="start.tb_id">์•„์ด๋””</td>
895
+ <td><code>admin</code></td>
896
+ </tr>
897
+ <tr>
898
+ <td data-i18n="start.tb_pw">๋น„๋ฐ€๋ฒˆํ˜ธ</td>
899
+ <td><code>admin123</code></td>
900
+ </tr>
901
+ <tr>
902
+ <td data-i18n="setup.tb_role">๋“ฑ๊ธ‰</td>
903
+ <td><code>admin</code></td>
904
+ </tr>
905
+ </tbody>
906
+ </table>
907
+
908
+ <h3 data-i18n="setup.group">5-2. ๊ณต์žฅ ๊ทธ๋ฃน ๋ฐ ๋กœ์ปฌ ํ”„๋กœํŒŒ์ผ ํ• ๋‹น</h3>
909
+ <p data-i18n="setup.group_desc">์–ด๋“œ๋ฏผ ๋กœ๊ทธ์ธ ์™„๋ฃŒ ํ›„ <strong>[๊ทธ๋ฃน ๊ด€๋ฆฌ]</strong> ํƒญ์œผ๋กœ ์ง„์ž…ํ•˜์—ฌ ์ƒˆ ์กฐ์ง์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ์‚ฌ ์ค‘์•™ ์„œ๋ฒ„๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ๊ฐ ๋กœ์ปฌ ๊ณต์žฅ(์˜ˆ: ์ฒญ์ฃผ๊ณต์žฅ, ํ—๊ฐ€๋ฆฌ๊ณต์žฅ, ๋ฏธ๊ตญ๊ณต์žฅ)๋งˆ๋‹ค ๋กœ์ปฌ DB ์ ‘์† ๋ฌธ์ž์—ด ๋ฐ ๋กœ์ปฌ MinIO/Neo4j ์ •๋ณด๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ž…๋ ฅํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์™„๋ฒฝํžˆ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
910
+ </section>
911
+
912
+ <!-- 6. Troubleshooting -->
913
+ <section class="section" id="troubleshooting">
914
+ <h2 data-i18n="trouble.title">6. ๋ฌธ์ œ ํ•ด๊ฒฐ (Troubleshooting)</h2>
915
+
916
+ <div class="card warn">
917
+ <h4 data-i18n="trouble.q1">๐Ÿ”ด Docker ์ปจํ…Œ์ด๋„ˆ ๊ตฌ๋™ ์‹œ Permission Denied ๋ฐœ์ƒ</h4>
918
+ <p data-i18n="trouble.a1">์‚ฌ์šฉ์ž ๊ณ„์ •์ด docker ๊ถŒํ•œ ๊ทธ๋ฃน์— ํฌํ•จ๋˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. <code>sudo usermod -aG docker $USER</code> ๋ช…๋ น์„ ์ž…๋ ฅํ•œ ํ›„, ์‰˜ ํ„ฐ๋ฏธ๋„ ์„ธ์…˜์„ ์™„์ „ํžˆ ์ข…๋ฃŒํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด ์‹œ์ž‘ํ•˜์‹ญ์‹œ์˜ค.</p>
919
+ </div>
920
+
921
+ <div class="card warn">
922
+ <h4 data-i18n="trouble.q2">๐Ÿ”ด "ECONNREFUSED" ์„œ๋ฒ„ ์‘๋‹ต ์—๋Ÿฌ ์ˆ˜์‹ </h4>
923
+ <p data-i18n="trouble.a2">์ค‘์•™ ์„œ๋ฒ„ ์ธก์˜ API ์‹คํ–‰ ๋ฐ๋ชฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ธฐ๋™๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ํฌํŠธ๊ฐ€ ์ถฉ๋Œํ•œ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. <code>sudo systemctl status certbridge</code> ์ƒํƒœ ์กฐํšŒ๋ฅผ ํ†ตํ•ด systemd ์„œ๋น„์Šค ์ƒ์กด ์œ ๋ฌด๋ฅผ ๊ฒ€์‚ฌํ•˜์‹ญ์‹œ์˜ค.</p>
924
+ </div>
925
+
926
+ <div class="card warn">
927
+ <h4 data-i18n="trouble.q3">๐ŸŸก Windows SmartScreen ์ฐจ๋‹จ ํŒ์—…์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.</h4>
928
+ <p data-i18n="trouble.a3">๋ณธ ํŒจํ‚ค์ง€๋Š” ๋„๋ฉ”์ธ ํ”„๋ผ์ด๋น— ๊ฐœ๋ฐœ ๋ณธ์œผ๋กœ ์ฝ”๋“œ ์‚ฌ์ด๋‹(Code Signing) ์„œ๋ช…์„œ๊ฐ€ ๊ฒฐ์—ฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ์—… ํ•˜๋‹จ์˜ "์ถ”๊ฐ€ ์ •๋ณด"๋ฅผ ํด๋ฆญํ•œ ๋‹ค์Œ, ๋‚˜ํƒ€๋‚œ "์‹คํ–‰" ๋ฒ„ํŠผ์„ ์•ˆ์‹ฌํ•˜๊ณ  ํด๋ฆญํ•ด ์…‹์—…์„ ์™„์ˆ˜ํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.</p>
929
+ </div>
930
+ </section>
931
+
932
+ </div>
933
+ </div>
934
+
935
+ <script>
936
+ // โ”€โ”€โ”€ Theme Synchronization โ”€โ”€โ”€
937
+ function syncTheme() {
938
+ let theme = "light";
939
+ try {
940
+ if (window.parent && window.parent.document && window.parent.document.documentElement) {
941
+ theme = window.parent.document.documentElement.getAttribute("data-theme") || "light";
942
+ } else {
943
+ theme = localStorage.getItem("certbridge_theme") || "light";
944
+ }
945
+ } catch (e) {
946
+ theme = localStorage.getItem("certbridge_theme") || "light";
947
+ }
948
+
949
+ if (theme === "dark") {
950
+ document.documentElement.setAttribute("data-theme", "dark");
951
+ } else {
952
+ document.documentElement.removeAttribute("data-theme");
953
+ }
954
+ }
955
+ syncTheme();
956
+ // Observe parent document changes if available
957
+ try {
958
+ if (window.parent && window.parent.document && window.parent.document.documentElement) {
959
+ const observer = new MutationObserver((mutations) => {
960
+ mutations.forEach((mutation) => {
961
+ if (mutation.type === "attributes" && mutation.attributeName === "data-theme") {
962
+ syncTheme();
963
+ }
964
+ });
965
+ });
966
+ observer.observe(window.parent.document.documentElement, { attributes: true });
967
+ }
968
+ } catch (e) {
969
+ setInterval(syncTheme, 1000);
970
+ }
971
+
972
+ // โ”€โ”€โ”€ Iframe Detection for Sidebar โ”€โ”€โ”€
973
+ if (window.self !== window.top) {
974
+ document.body.classList.add('in-iframe');
975
+ }
976
+
977
+ // 9 Locales Translation Dictionary for Installation/Setup Guide
978
+ const i18n = {
979
+ ko: {
980
+ "sidebar.ver": "์„ค์น˜ ๊ฐ€์ด๋“œ v0.1.0",
981
+ "nav.req": "์‹œ์Šคํ…œ ์š”๊ตฌ์‚ฌํ•ญ",
982
+ "nav.server": "์„œ๋ฒ„ ์„ค์น˜ (Ubuntu)",
983
+ "nav.client": "ํด๋ผ์ด์–ธํŠธ ์„ค์น˜ (Windows)",
984
+ "nav.network": "๋„คํŠธ์›Œํฌ ๊ตฌ์„ฑ",
985
+ "nav.setup": "์ดˆ๊ธฐ ์„ค์ •",
986
+ "nav.trouble": "๋ฌธ์ œ ํ•ด๊ฒฐ",
987
+ "hero.title": "CertBridge ์„ค์น˜ ๋ฐ ์…‹์—… ๊ฐ€์ด๋“œ",
988
+ "hero.desc": "AI ์„ฑ์ ์„œ ์ž๋™ ํŒŒ์‹ฑ ๋ฐ ๋ ˆ๊ฑฐ์‹œ ํ†ตํ•ฉ ์‹œ์Šคํ…œ์„ Ubuntu ์„œ๋ฒ„์™€ Windows ํด๋ผ์ด์–ธํŠธ์— ๊ตฌ์ถ•ํ•˜๋Š” ์ƒ์„ธ ํ”„๋กœํ† ์ฝœ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.",
989
+ "hero.badge": "๐Ÿ“ฆ v0.1.0 ยท Ubuntu 22.04+ ยท Windows 10+",
990
+ "req.title": "1. ์‹œ์Šคํ…œ ์š”๊ตฌ์‚ฌํ•ญ",
991
+ "req.server_title": "๐Ÿ–ฅ๏ธ ์„œ๋ฒ„ ํ•˜๋“œ์›จ์–ด ๊ถŒ์žฅ์‚ฌ์–‘ (Ubuntu OS)",
992
+ "req.os": "์šด์˜์ฒด์ œ",
993
+ "req.ram": "๋ฉ”๋ชจ๋ฆฌ",
994
+ "req.disk": "๋””๋ฐ”์ด์Šค ์šฉ๋Ÿ‰",
995
+ "req.net": "๋„คํŠธ์›Œํฌ๋ง",
996
+ "req.client_title": "๐Ÿ’ป ํด๋ผ์ด์–ธํŠธ ์‹คํ–‰ ํ™˜๊ฒฝ (Windows)",
997
+ "req.software": "๐Ÿ”ง ํ•„์ˆ˜ ์ธํ”„๋ผ ์†Œํ”„ํŠธ์›จ์–ด (์„œ๋ฒ„ ์ธก)",
998
+ "req.tb_soft": "์†Œํ”„ํŠธ์›จ์–ด",
999
+ "req.tb_ver": "์ตœ์†Œ ๋ฒ„์ „",
1000
+ "req.tb_usage": "์šฉ๋„",
1001
+ "req.tb_docker_desc": "์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ฐ ์บ์‹œ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜",
1002
+ "req.tb_compose_desc": "๋ฉ€ํ‹ฐ ์ปจํ…Œ์ด๋„ˆ ์ธํ”„๋ผ ์Šคํƒ ํ†ตํ•ฉ ์‹คํ–‰",
1003
+ "req.tb_node_desc": "CertBridge Express API ์ฝ”์–ด ์„œ๋ฒ„ ๊ตฌ๋™",
1004
+ "server.title": "2. ์„œ๋ฒ„ ์„ค์น˜ (Ubuntu)",
1005
+ "server.auto": "2-1. ์ž๋™ ์‰˜ ์Šคํฌ๋ฆฝํŠธ ์„ค์น˜ (๊ถŒ์žฅ)",
1006
+ "server.auto_desc": "์ œ๊ณต๋œ setup.sh ํŒŒ์ผ์„ ํ™œ์šฉํ•ด ๋„์ปค ์—”์ง„, ํ™˜๊ฒฝ๋ณ€์ˆ˜, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, systemd ์„œ๋น„์Šค ๋“ฑ๋ก์„ ํ•œ ๋ฒˆ์— ์ž๋™์œผ๋กœ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.",
1007
+ "server.manual": "2-2. ๋‹จ๊ณ„๋ณ„ ์ˆ˜๋™ ์„ค์น˜ ํ”„๋กœํ† ์ฝœ",
1008
+ "server.d_setup": "Docker ์„ค์น˜ ๋ฐ ์‹คํ–‰ ํ™˜๊ฒฝ ๊ตฌ์„ฑ",
1009
+ "server.compose": "๋„์ปค ์ปดํฌ์ฆˆ ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ๊ตฌ์„ฑ์š”์†Œ ์‹คํ–‰",
1010
+ "server.compose_desc": "PostgreSQL, Redis, MinIO ์˜ค๋ธŒ์ ํŠธ ์Šคํ† ๋ฆฌ์ง€ ๋ฐ Neo4j ๊ทธ๋ž˜ํ”„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ•˜๋‚˜์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šคํƒ์œผ๋กœ ๋„์›๋‹ˆ๋‹ค.",
1011
+ "server.env_setup": "API ํ™˜๊ฒฝ ํŒŒ์ผ ๊ตฌ์„ฑ (.env)",
1012
+ "server.warn_title": "โš ๏ธ ํ•„์ˆ˜ ๋ณด์•ˆ ์ฃผ์˜์‚ฌํ•ญ",
1013
+ "server.warn_desc": "์šด์˜ ํ™˜๊ฒฝ ๋ฐฐํฌ ์‹œ, JWT_SECRET ์•”ํ˜ธํ™” ํ‚ค์™€ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒจ์Šค์›Œ๋“œ ๋ฐ MinIO ACCESS/SECRET ํ‚ค๋ฅผ ๋‚œ์ˆ˜ ํ˜•ํƒœ๋กœ ๋ฐ˜๋“œ์‹œ ๊ต์ฒดํ•ด ์ ์šฉํ•˜์‹ญ์‹œ์˜ค.",
1014
+ "server.api_start": "API ์„œ๋ฒ„ ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ๊ธฐ๋™",
1015
+ "client.title": "3. ํด๋ผ์ด์–ธํŠธ ์„ค์น˜ (Windows)",
1016
+ "client.s1_title": "์„ค์น˜ ํŒŒ์ผ ํŒจํ‚ค์ง€ ๋‹ค์šด๋กœ๋“œ",
1017
+ "client.s1_desc": "๊ณต์‹ ๋ฆด๋ฆฌ์ฆˆ ๋ณด๊ด€์†Œ(HuggingFace ๋˜๋Š” ํšŒ์‚ฌ CDN๋ง)์—์„œ CertBridge_0.1.0_x64-setup.exe(์ž๋™ ๋นŒ๋“œ ๋ณธ)๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.",
1018
+ "client.s2_title": "์„ค์น˜ ๋งˆ๋ฒ•์‚ฌ ์ง„ํ–‰ ๋ฐ ์–ธ์–ด ํƒ€๊ฒŸํŒ…",
1019
+ "client.s2_desc": "์„ค์น˜ ๋งˆ๋ฒ•์‚ฌ ๋‹จ๊ณ„์—์„œ ์›ํ•˜๋Š” ์–ธ์–ด๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ค์ •(ํ•œ๊ธ€/์˜์–ด ์ž๋™ ๋งค์นญ)ํ•œ ํ›„, ์ง€์ •๋œ ๊ฒฝ๋กœ์— ํด๋ผ์ด์–ธํŠธ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋นŒ๋“œ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.",
1020
+ "client.s3_title": "์ค‘์•™ ์—”๋“œํฌ์ธํŠธ ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ",
1021
+ "client.s3_desc": "๋ฐ”ํƒ•ํ™”๋ฉด ๋‹จ์ถ• ์•„์ด์ฝ˜์„ ํ†ตํ•ด ์‹คํ–‰ ํ›„, ์šฐ์ธก ์•„๋ž˜์˜ ์„ค์ • ์•„์ด์ฝ˜(โš™๏ธ)์„ ํด๋ฆญํ•ด ์ค‘์•™์— ๊ฐœ๋ฐฉ๋œ API ์„œ๋ฒ„ IP ๋ฐ ๋„๋ฉ”์ธ์„ ์ž…๋ ฅํ•˜์—ฌ ํ…Œ์ŠคํŠธ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค: http://<์ค‘์•™์„œ๋ฒ„_IP>:8090",
1022
+ "net.title": "4. ๋„คํŠธ์›Œํฌ ๋ฐ ํฌํŠธ ๊ตฌ์„ฑ",
1023
+ "net.ports": "์ธํ”„๋ผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ ํฌํŠธ ํ˜„ํ™ฉ",
1024
+ "net.tb_serv": "์—ฐ๋™ ์„œ๋น„์Šค",
1025
+ "net.tb_port": "๊ธฐ๋ณธ ํฌํŠธ",
1026
+ "net.tb_proto": "ํ”„๋กœํ† ์ฝœ",
1027
+ "net.tb_desc": "์„ค๋ช…",
1028
+ "net.tb_p1": "์ฝ”์–ด ํด๋ผ์ด์–ธํŠธ REST & ํ•˜ํŠธ๋น„ํŠธ ์—ฐ๊ฒฐ ์ฐฝ๊ตฌ (์ธ๋ฐ”์šด๋“œ ํ—ˆ์šฉ ํ•„์ˆ˜)",
1029
+ "net.tb_p2": "ํ’ˆ์งˆ ์ง€์‹ ์˜จํ†จ๋กœ์ง€ ๊ณ ์† ์ปค๋„ฅํ„ฐ ํฌํŠธ",
1030
+ "net.tb_p3": "์„ฑ์ ์„œ PDF ๋ฌผ๋ฆฌ ์›๋ณธ ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์Šคํ† ๋ฆฌ์ง€ API",
1031
+ "net.tb_p4": "์›Œํฌํ”Œ๋กœ์šฐ ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ ํŠธ๋ฆฌ๊ฑฐ ์—”๋“œํฌ์ธํŠธ",
1032
+ "net.firewall": "Ubuntu ๋ฐฉํ™”๋ฒฝ (UFW) ์„ค์ • ์Šคํฌ๋ฆฝํŠธ",
1033
+ "setup.title": "5. ์ค‘์•™ ๊ด€๋ฆฌ ํ™˜๊ฒฝ ์ดˆ๊ธฐํ™”",
1034
+ "setup.admin": "5-1. ์ตœ์ดˆ ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ •๋ณด",
1035
+ "setup.admin_desc": "์„ค์น˜๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋งˆ์ณ์ง€๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ๋“œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์•„๋ž˜ ๊ด€๋ฆฌ์ž ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:",
1036
+ "setup.tb_col": "์„ค์ • ์ •๋ณด",
1037
+ "setup.tb_val": "์ง€์ •๋œ ๊ธฐ๋ณธ ๊ฐ’",
1038
+ "setup.tb_role": "๋“ฑ๊ธ‰",
1039
+ "setup.group": "5-2. ๊ณต์žฅ ๊ทธ๋ฃน ๋ฐ ๋กœ์ปฌ ํ”„๋กœํŒŒ์ผ ํ• ๋‹น",
1040
+ "setup.group_desc": "์–ด๋“œ๋ฏผ ๋กœ๊ทธ์ธ ์™„๋ฃŒ ํ›„ [๊ทธ๋ฃน ๊ด€๋ฆฌ] ํƒญ์œผ๋กœ ์ง„์ž…ํ•˜์—ฌ ์ƒˆ ์กฐ์ง์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ์‚ฌ ์ค‘์•™ ์„œ๋ฒ„๋ฅผ ์œ ์ง€ํ•˜๏ฟฝ๏ฟฝ๏ฟฝ์„œ ๊ฐ ๋กœ์ปฌ ๊ณต์žฅ(์˜ˆ: ์ฒญ์ฃผ๊ณต์žฅ, ํ—๊ฐ€๋ฆฌ๊ณต์žฅ, ๋ฏธ๊ตญ๊ณต์žฅ)๋งˆ๋‹ค ๋กœ์ปฌ DB ์ ‘์† ๋ฌธ์ž์—ด ๋ฐ ๋กœ์ปฌ MinIO/Neo4j ์ •๋ณด๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ž…๋ ฅํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์™„๋ฒฝํžˆ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
1041
+ "trouble.title": "6. ๋ฌธ์ œ ํ•ด๊ฒฐ (Troubleshooting)",
1042
+ "trouble.q1": "๐Ÿ”ด Docker ์ปจํ…Œ์ด๋„ˆ ๊ตฌ๋™ ์‹œ Permission Denied ๋ฐœ์ƒ",
1043
+ "trouble.a1": "์‚ฌ์šฉ์ž ๊ณ„์ •์ด docker ๊ถŒํ•œ ๊ทธ๋ฃน์— ํฌํ•จ๋˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. sudo usermod -aG docker $USER ๋ช…๋ น์„ ์ž…๋ ฅํ•œ ํ›„, ์‰˜ ํ„ฐ๋ฏธ๋„ ์„ธ์…˜์„ ์™„์ „ํžˆ ์ข…๋ฃŒํ–ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด ์‹œ์ž‘ํ•˜์‹ญ์‹œ์˜ค.",
1044
+ "trouble.q2": "๐Ÿ”ด \"ECONNREFUSED\" ์„œ๋ฒ„ ์‘๋‹ต ์—๋Ÿฌ ์ˆ˜์‹ ",
1045
+ "trouble.a2": "์ค‘์•™ ์„œ๋ฒ„ ์ธก์˜ API ์‹คํ–‰ ๋ฐ๋ชฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ธฐ๋™๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ํฌํŠธ๊ฐ€ ์ถฉ๋Œํ•œ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. sudo systemctl status certbridge ์ƒํƒœ ์กฐํšŒ๋ฅผ ํ†ตํ•ด systemd ์„œ๋น„์Šค ์ƒ์กด ์œ ๋ฌด๋ฅผ ๊ฒ€์‚ฌํ•˜์‹ญ์‹œ์˜ค.",
1046
+ "trouble.q3": " Windows SmartScreen ์ฐจ๋‹จ ํŒ์—…์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.",
1047
+ "trouble.a3": "๋ณธ ํŒจํ‚ค์ง€๋Š” ๋„๋ฉ”์ธ ํ”„๋ผ์ด๋น— ๊ฐœ๋ฐœ ๋ณธ์œผ๋กœ ์ฝ”๋“œ ์‚ฌ์ด๋‹(Code Signing) ์„œ๋ช…์„œ๊ฐ€ ๊ฒฐ์—ฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŒ์—… ํ•˜๋‹จ์˜ \"์ถ”๊ฐ€ ์ •๋ณด\"๋ฅผ ํด๋ฆญํ•œ ๋‹ค์Œ, ๋‚˜ํƒ€๋‚œ \"์‹คํ–‰\" ๋ฒ„ํŠผ์„ ์•ˆ์‹ฌํ•˜๊ณ  ํด๋ฆญํ•ด ์…‹์—…์„ ์™„์ˆ˜ํ•ด ์ฃผ์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค."
1048
+ },
1049
+ en: {
1050
+ "sidebar.ver": "Installation Guide v0.1.0",
1051
+ "nav.req": "System Requirements",
1052
+ "nav.server": "Server Setup (Ubuntu)",
1053
+ "nav.client": "Client Setup (Windows)",
1054
+ "nav.network": "Network Config",
1055
+ "nav.setup": "Initial Config",
1056
+ "nav.trouble": "Troubleshooting",
1057
+ "hero.title": "CertBridge Server Deployment Guide",
1058
+ "hero.desc": "Step-by-step instructions to configure the central system environment on an Ubuntu server and set up Windows desktop clients.",
1059
+ "hero.badge": "๐Ÿ“ฆ v0.1.0 ยท Ubuntu 22.04+ ยท Windows 10+",
1060
+ "req.title": "1. System Requirements",
1061
+ "req.server_title": "๐Ÿ–ฅ๏ธ Server Configuration (Ubuntu OS)",
1062
+ "req.os": "Operating System",
1063
+ "req.ram": "RAM",
1064
+ "req.disk": "Disk Space",
1065
+ "req.net": "Network Connection",
1066
+ "req.client_title": "๐Ÿ’ป Client Environment (Windows)",
1067
+ "req.software": "๐Ÿ”ง Infrastructure Prerequisites (Server)",
1068
+ "req.tb_soft": "Software",
1069
+ "req.tb_ver": "Min Version",
1070
+ "req.tb_usage": "Usage",
1071
+ "req.tb_docker_desc": "Containerized databases and queue storage orchestration",
1072
+ "req.tb_compose_desc": "Multi-container environment running under unified stacks",
1073
+ "req.tb_node_desc": "CertBridge core Express API gateway server execution",
1074
+ "server.title": "2. Server Installation (Ubuntu)",
1075
+ "server.auto": "2-1. Automatic Shell Script Installation (Recommended)",
1076
+ "server.auto_desc": "Use setup.sh to automatically configure Docker, generate required secrets, connect databases, and register the systemd background service.",
1077
+ "server.manual": "2-2. Manual Step-by-Step Installation",
1078
+ "server.d_setup": "Install Docker Engine",
1079
+ "server.compose": "Launch Multi-Container Infrastructure Stack",
1080
+ "server.compose_desc": "Launch PostgreSQL, Redis, MinIO object storage, and Neo4j graph database as a single background service stack.",
1081
+ "server.env_setup": "API Configuration File (.env)",
1082
+ "server.warn_title": "โš ๏ธ Production Security Notice",
1083
+ "server.warn_desc": "Make sure to change JWT_SECRET keys, default database passwords, and MinIO root credential values before deploying in a production network.",
1084
+ "server.api_start": "Install Dependencies and Start API Gateway",
1085
+ "client.title": "3. Desktop Client Setup (Windows)",
1086
+ "client.s1_title": "Download Installer Package",
1087
+ "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.",
1088
+ "client.s2_title": "Install & Choose Language Profile",
1089
+ "client.s2_desc": "Follow the wizard setup steps, verify the target path, select your default active language, and complete the installation.",
1090
+ "client.s3_title": "Central Connection Handshake Test",
1091
+ "client.s3_desc": "Open the application, click the Settings (โš™๏ธ) icon, and enter the active central API server endpoint address: http://<server_ip>:8090",
1092
+ "net.title": "4. Network & Ports Configurations",
1093
+ "net.ports": "System Port Allocations",
1094
+ "net.tb_serv": "Linked Service",
1095
+ "net.tb_port": "Default Port",
1096
+ "net.tb_proto": "Protocol",
1097
+ "net.tb_desc": "Description",
1098
+ "net.tb_p1": "Core client REST & Heartbeat channel (Inbound access required)",
1099
+ "net.tb_p2": "Neo4j Bolt protocol high-speed graph driver interface",
1100
+ "net.tb_p3": "S3-compatible API for original PDF quality certificate storage",
1101
+ "net.tb_p4": "n8n automation pipeline webhook gateway",
1102
+ "net.firewall": "Ubuntu Firewall (UFW) Configuration Script",
1103
+ "setup.title": "5. Initial Admin Configuration",
1104
+ "setup.admin": "5-1. Default Administrator Credentials",
1105
+ "setup.admin_desc": "The DB seeding script creates the default admin user account as follows:",
1106
+ "setup.tb_col": "Property",
1107
+ "setup.tb_val": "Default Value",
1108
+ "setup.tb_role": "Role",
1109
+ "setup.group": "5-2. Group Isolation & Local Infrastructure Profiles",
1110
+ "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.",
1111
+ "trouble.title": "6. Troubleshooting Guidelines",
1112
+ "trouble.q1": "๐Ÿ”ด Docker: Permission Denied Error",
1113
+ "trouble.a1": "Add your user account to the docker permission group using: sudo usermod -aG docker $USER. Restart your terminal session and rerun setup.",
1114
+ "trouble.q2": "๐Ÿ”ด 'ECONNREFUSED' Connection Error",
1115
+ "trouble.a2": "Check if the API daemon process is running on port 8090. Run: sudo systemctl status certbridge to verify the systemd background state.",
1116
+ "trouble.q3": "๐ŸŸก Windows SmartScreen Popup Block Warning",
1117
+ "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."
1118
+ }
1119
+ };
1120
+
1121
+ function changeLanguage(lang) {
1122
+ document.documentElement.lang = lang;
1123
+ localStorage.setItem("certbridge_language", lang);
1124
+ document.getElementById("lang-select").value = lang;
1125
+
1126
+ // Update all elements with data-i18n attribute
1127
+ const elements = document.querySelectorAll("[data-i18n]");
1128
+ elements.forEach(el => {
1129
+ const key = el.getAttribute("data-i18n");
1130
+ if (i18n[lang] && i18n[lang][key]) {
1131
+ el.innerHTML = i18n[lang][key];
1132
+ } else if (i18n["en"] && i18n["en"][key]) {
1133
+ el.innerHTML = i18n["en"][key];
1134
+ }
1135
+ });
1136
+
1137
+ // Update HTML Title dynamically
1138
+ if (lang === "ko") {
1139
+ document.getElementById("html-title").innerText = "CertBridge ์„ค์น˜ ๋ฐ ์…‹์—… ๊ฐ€์ด๋“œ";
1140
+ } else if (lang === "zh") {
1141
+ document.getElementById("html-title").innerText = "CertBridge ๅฎ‰่ฃ…ไธŽ้…็ฝฎๆŒ‡ๅ—";
1142
+ } else if (lang === "ja") {
1143
+ document.getElementById("html-title").innerText = "CertBridge ๆง‹็ฏ‰ใƒป่จญๅฎšใ‚ฌใ‚คใƒ‰";
1144
+ } else if (lang === "hu") {
1145
+ document.getElementById("html-title").innerText = "CertBridge Szervertelepรญtรฉsi รštmutatรณ";
1146
+ } else if (lang === "de") {
1147
+ document.getElementById("html-title").innerText = "CertBridge Server-Bereitstellungshandbuch";
1148
+ } else if (lang === "fr") {
1149
+ document.getElementById("html-title").innerText = "Guide de dรฉploiement serveur CertBridge";
1150
+ } else if (lang === "es") {
1151
+ document.getElementById("html-title").innerText = "Guรญa de despliegue de servidor CertBridge";
1152
+ } else if (lang === "pt") {
1153
+ document.getElementById("html-title").innerText = "Guia de implantaรงรฃo de servidor CertBridge";
1154
+ } else {
1155
+ document.getElementById("html-title").innerText = "CertBridge Server Deployment Guide";
1156
+ }
1157
+
1158
+ console.log(`๐ŸŒ Setup Guide Language updated to: ${lang}`);
1159
+ }
1160
+
1161
+ // Initializer
1162
+ document.addEventListener("DOMContentLoaded", () => {
1163
+ const getLang = () => localStorage.getItem("certbridge_language") || "ko";
1164
+ changeLanguage(getLang());
1165
+
1166
+ // Storage listener for instant sync
1167
+ window.addEventListener("storage", (e) => {
1168
+ if (e.key === "certbridge_language") {
1169
+ changeLanguage(e.newValue || "ko");
1170
+ }
1171
+ });
1172
+
1173
+ // Polling fallback
1174
+ setInterval(() => {
1175
+ const currentLang = document.documentElement.lang;
1176
+ const targetLang = getLang();
1177
+ if (currentLang !== targetLang) {
1178
+ changeLanguage(targetLang);
1179
+ }
1180
+ }, 500);
1181
+
1182
+ // ScrollSpy logic
1183
+ const sections = document.querySelectorAll('.section');
1184
+ const navLinks = document.querySelectorAll('#nav a');
1185
+ const observer = new IntersectionObserver(entries => {
1186
+ entries.forEach(e => {
1187
+ if (e.isIntersecting) {
1188
+ navLinks.forEach(l => l.classList.remove('active'));
1189
+ const id = e.target.id;
1190
+ const link = document.querySelector(`#nav a[href="#${id}"]`);
1191
+ if (link) link.classList.add('active');
1192
+ }
1193
+ });
1194
+ }, { threshold: 0.2 });
1195
+ sections.forEach(s => observer.observe(s));
1196
+ });
1197
+
1198
+ // Copy to clipboard helper
1199
+ function copyCode(btn) {
1200
+ const pre = btn.closest('.code-wrap').querySelector('pre');
1201
+ navigator.clipboard.writeText(pre.textContent).then(() => {
1202
+ const lang = document.documentElement.lang || "ko";
1203
+ const msg = lang === "ko" ? "๋ณต์‚ฌ๋จ!" : "Copied!";
1204
+ const originalText = btn.textContent;
1205
+ btn.textContent = msg;
1206
+ setTimeout(() => {
1207
+ btn.textContent = originalText;
1208
+ }, 2000);
1209
+ });
1210
+ }
1211
+ </script>
1212
+ </body>
1213
+ </html>