Initial commit to new repository
This commit is contained in:
754
src/AxKeyEncryptor/MainForm.cs
Normal file
754
src/AxKeyEncryptor/MainForm.cs
Normal file
@@ -0,0 +1,754 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AxKeyEncryptor;
|
||||
|
||||
/// <summary>
|
||||
/// AX Copilot — Settings Encryptor (개발자 전용)
|
||||
/// Ollama / vLLM / Gemini 3종 서비스에 대한 설정값 암호화 UI
|
||||
/// </summary>
|
||||
public class MainForm : Form
|
||||
{
|
||||
// ── 색상 테마 ───────────────────────────────────────────────────
|
||||
private static readonly Color BgDark = Color.FromArgb(24, 24, 32);
|
||||
private static readonly Color BgPanel = Color.FromArgb(32, 34, 46);
|
||||
private static readonly Color BgCard = Color.FromArgb(42, 44, 58);
|
||||
private static readonly Color BgInput = Color.FromArgb(52, 54, 70);
|
||||
private static readonly Color AccentBlue = Color.FromArgb(88, 130, 255);
|
||||
private static readonly Color AccentGreen = Color.FromArgb(80, 200, 120);
|
||||
private static readonly Color AccentOrange= Color.FromArgb(255, 160, 60);
|
||||
private static readonly Color AccentRed = Color.FromArgb(255, 90, 90);
|
||||
private static readonly Color TextPrimary = Color.FromArgb(230, 232, 240);
|
||||
private static readonly Color TextSecondary= Color.FromArgb(140, 145, 170);
|
||||
private static readonly Color BorderColor = Color.FromArgb(60, 64, 80);
|
||||
|
||||
// ── 서비스별 탭 컨트롤 ──────────────────────────────────────────
|
||||
private TabControl _tabs = null!;
|
||||
|
||||
// Ollama 탭 컨트롤
|
||||
private TextBox _ollamaEndpoint = null!;
|
||||
private TextBox _ollamaModel = null!;
|
||||
private TextBox _ollamaEncEndpoint = null!;
|
||||
private TextBox _ollamaEncModel = null!;
|
||||
|
||||
// vLLM 탭 컨트롤
|
||||
private TextBox _vllmEndpoint = null!;
|
||||
private TextBox _vllmModel = null!;
|
||||
private TextBox _vllmApiKey = null!;
|
||||
private TextBox _vllmEncEndpoint = null!;
|
||||
private TextBox _vllmEncModel = null!;
|
||||
private TextBox _vllmEncApiKey = null!;
|
||||
|
||||
// Gemini 탭 컨트롤
|
||||
private TextBox _geminiApiKey = null!;
|
||||
private TextBox _geminiModel = null!;
|
||||
private TextBox _geminiEncApiKey = null!;
|
||||
private TextBox _geminiEncModel = null!;
|
||||
|
||||
// 하단 공용
|
||||
private RichTextBox _outputBox = null!;
|
||||
private TextBox _decryptInput = null!;
|
||||
private TextBox _decryptResult = null!;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
// ── 폼 기본 설정 ────────────────────────────────────────
|
||||
Text = "AX Copilot — Settings Encryptor";
|
||||
Size = new Size(760, 680);
|
||||
MinimumSize = new Size(720, 620);
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
BackColor = BgDark;
|
||||
ForeColor = TextPrimary;
|
||||
Font = new Font("Segoe UI", 9.5f, FontStyle.Regular);
|
||||
FormBorderStyle = FormBorderStyle.FixedSingle;
|
||||
MaximizeBox = false;
|
||||
|
||||
// 아이콘 설정
|
||||
try
|
||||
{
|
||||
var asm = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
var stream = asm.GetManifestResourceStream("AxKeyEncryptor.Resources.icon.ico");
|
||||
if (stream != null) Icon = new Icon(stream);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// ── 헤더 패널 ──────────────────────────────────────────
|
||||
var headerPanel = new Panel
|
||||
{
|
||||
Dock = DockStyle.Top,
|
||||
Height = 60,
|
||||
BackColor = Color.Transparent
|
||||
};
|
||||
headerPanel.Paint += (s, e) =>
|
||||
{
|
||||
using var brush = new LinearGradientBrush(
|
||||
headerPanel.ClientRectangle,
|
||||
Color.FromArgb(35, 40, 65),
|
||||
Color.FromArgb(24, 24, 32),
|
||||
LinearGradientMode.Vertical);
|
||||
e.Graphics.FillRectangle(brush, headerPanel.ClientRectangle);
|
||||
|
||||
// 타이틀
|
||||
using var titleFont = new Font("Segoe UI", 14f, FontStyle.Bold);
|
||||
e.Graphics.DrawString("🔐 AX Copilot — Settings Encryptor",
|
||||
titleFont, new SolidBrush(TextPrimary), 20, 8);
|
||||
|
||||
// 서브타이틀
|
||||
using var subFont = new Font("Segoe UI", 8.5f, FontStyle.Regular);
|
||||
e.Graphics.DrawString("AES-256-CBC 앱 공용 키 암호화 · 모든 PC에서 동일 복호화 · 개발자 전용",
|
||||
subFont, new SolidBrush(TextSecondary), 48, 36);
|
||||
};
|
||||
Controls.Add(headerPanel);
|
||||
|
||||
// ── 메인 컨텐츠 패널 ────────────────────────────────────
|
||||
var mainPanel = new Panel
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
Padding = new Padding(16, 8, 16, 16),
|
||||
BackColor = BgDark
|
||||
};
|
||||
Controls.Add(mainPanel);
|
||||
|
||||
// ── 서비스 선택 탭 ──────────────────────────────────────
|
||||
_tabs = new TabControl
|
||||
{
|
||||
Dock = DockStyle.Top,
|
||||
Height = 260,
|
||||
Font = new Font("Segoe UI", 10f, FontStyle.Regular),
|
||||
Padding = new Point(12, 6)
|
||||
};
|
||||
_tabs.DrawMode = TabDrawMode.OwnerDrawFixed;
|
||||
_tabs.DrawItem += DrawTab;
|
||||
_tabs.SizeMode = TabSizeMode.Fixed;
|
||||
_tabs.ItemSize = new Size(160, 36);
|
||||
|
||||
// Ollama 탭
|
||||
var ollamaPage = CreateTabPage("🟢 Ollama", AccentGreen);
|
||||
BuildOllamaTab(ollamaPage);
|
||||
_tabs.TabPages.Add(ollamaPage);
|
||||
|
||||
// vLLM 탭
|
||||
var vllmPage = CreateTabPage("🔵 vLLM (OpenAI)", AccentBlue);
|
||||
BuildVllmTab(vllmPage);
|
||||
_tabs.TabPages.Add(vllmPage);
|
||||
|
||||
// Gemini 탭
|
||||
var geminiPage = CreateTabPage("🟠 Gemini", AccentOrange);
|
||||
BuildGeminiTab(geminiPage);
|
||||
_tabs.TabPages.Add(geminiPage);
|
||||
|
||||
// 복호화 검증 탭 추가
|
||||
var toolsPage = CreateTabPage("🔓 복호화 검증", AccentBlue);
|
||||
BuildToolsTab(toolsPage);
|
||||
_tabs.TabPages.Add(toolsPage);
|
||||
|
||||
mainPanel.Controls.Add(_tabs);
|
||||
|
||||
// ── 출력 로그 섹션 ──────────────────────────────────────
|
||||
var outputLabel = CreateLabel("📋 출력 로그", 10f, FontStyle.Bold);
|
||||
outputLabel.Dock = DockStyle.Top;
|
||||
outputLabel.Height = 28;
|
||||
outputLabel.Padding = new Padding(4, 8, 0, 0);
|
||||
mainPanel.Controls.Add(outputLabel);
|
||||
|
||||
_outputBox = new RichTextBox
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
BackColor = Color.FromArgb(18, 18, 24),
|
||||
ForeColor = TextPrimary,
|
||||
Font = new Font("Cascadia Code", 9.5f, FontStyle.Regular, GraphicsUnit.Point, 0,
|
||||
// fallback if Cascadia Code not installed
|
||||
false),
|
||||
BorderStyle = BorderStyle.None,
|
||||
ReadOnly = true,
|
||||
WordWrap = true,
|
||||
Padding = new Padding(8)
|
||||
};
|
||||
// Cascadia Code fallback
|
||||
if (_outputBox.Font.Name != "Cascadia Code")
|
||||
_outputBox.Font = new Font("Consolas", 9.5f);
|
||||
mainPanel.Controls.Add(_outputBox);
|
||||
|
||||
// ── 하단 상태바 ─────────────────────────────────────────
|
||||
var statusBar = new Panel
|
||||
{
|
||||
Dock = DockStyle.Bottom,
|
||||
Height = 30,
|
||||
BackColor = Color.FromArgb(20, 22, 30)
|
||||
};
|
||||
var statusLabel = CreateLabel(" AES-256-CBC · PBKDF2 100K iterations · 모든 PC에서 동일한 암호화 키", 8f, FontStyle.Regular);
|
||||
statusLabel.ForeColor = TextSecondary;
|
||||
statusLabel.Dock = DockStyle.Fill;
|
||||
statusLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||||
statusBar.Controls.Add(statusLabel);
|
||||
Controls.Add(statusBar);
|
||||
|
||||
// ── 컨트롤 순서 (뒤에서 앞으로) ──────────────────────────
|
||||
// Panel DockStyle 순서 보장
|
||||
Controls.SetChildIndex(statusBar, 0);
|
||||
Controls.SetChildIndex(headerPanel, 0);
|
||||
|
||||
mainPanel.Controls.SetChildIndex(_outputBox, 0);
|
||||
mainPanel.Controls.SetChildIndex(outputLabel, 0);
|
||||
mainPanel.Controls.SetChildIndex(_tabs, 0);
|
||||
|
||||
AppendLog("AX Copilot Settings Encryptor 시작됨", AccentGreen);
|
||||
AppendLog("", TextSecondary);
|
||||
AppendLog("── 사용 방법 ──────────────────────────────────────────", AccentBlue);
|
||||
AppendLog("1. 상단 탭에서 LLM 서비스(Ollama/vLLM/Gemini)를 선택합니다.", TextPrimary);
|
||||
AppendLog("2. 각 필드에 평문 값(URL, 모델명, API 키)을 입력합니다.", TextPrimary);
|
||||
AppendLog("3. [⚡ 전체 암호화] 버튼으로 모든 필드를 한번에 암호화합니다.", TextPrimary);
|
||||
AppendLog("4. [📋 settings.json 복사] 버튼으로 암호화된 JSON을 클립보드에 복사합니다.", TextPrimary);
|
||||
AppendLog("5. 복사된 JSON을 settings.json 파일의 llm 섹션에 붙여넣으면 완료!", TextPrimary);
|
||||
AppendLog("", TextSecondary);
|
||||
AppendLog("── 암호화 방식 ────────────────────────────────────────", AccentBlue);
|
||||
AppendLog("• AES-256-CBC + PBKDF2(100K iterations, SHA256)", TextSecondary);
|
||||
AppendLog("• 앱 공용 키 방식 — 모든 PC에서 동일한 키로 복호화됩니다.", TextSecondary);
|
||||
AppendLog("• 배포 시 settings.json에 암호화된 값을 넣으면 앱이 자동 복호화합니다.", TextSecondary);
|
||||
AppendLog("", TextSecondary);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// Ollama 탭 빌드
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void BuildOllamaTab(TabPage page)
|
||||
{
|
||||
int y = 12;
|
||||
var desc = CreateLabel("Ollama는 로컬 LLM 서버입니다. API 키가 필요 없습니다.", 9f, FontStyle.Italic);
|
||||
desc.ForeColor = TextSecondary;
|
||||
desc.Location = new Point(12, y);
|
||||
desc.AutoSize = true;
|
||||
page.Controls.Add(desc);
|
||||
y += 30;
|
||||
|
||||
// Endpoint
|
||||
AddFieldPair(page, ref y, "Endpoint URL:", "http://localhost:11434",
|
||||
out _ollamaEndpoint, out _ollamaEncEndpoint, "암호화", AccentGreen, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_ollamaEndpoint, _ollamaEncEndpoint, "Ollama Endpoint");
|
||||
});
|
||||
|
||||
// Model
|
||||
AddFieldPair(page, ref y, "Model:", "llama3.1:8b",
|
||||
out _ollamaModel, out _ollamaEncModel, "암호화", AccentGreen, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_ollamaModel, _ollamaEncModel, "Ollama Model");
|
||||
});
|
||||
|
||||
// 전체 암호화 버튼
|
||||
var btnAll = CreateButton("⚡ 전체 암호화", AccentGreen);
|
||||
btnAll.Location = new Point(12, y + 4);
|
||||
btnAll.Size = new Size(130, 34);
|
||||
btnAll.Click += (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_ollamaEndpoint, _ollamaEncEndpoint, "Ollama Endpoint");
|
||||
EncryptAndShow(_ollamaModel, _ollamaEncModel, "Ollama Model");
|
||||
AppendLog("[Ollama] 전체 암호화 완료 ✔", AccentGreen);
|
||||
};
|
||||
page.Controls.Add(btnAll);
|
||||
|
||||
// JSON 생성 버튼
|
||||
var btnJson = CreateButton("📋 settings.json 복사", AccentGreen);
|
||||
btnJson.Location = new Point(150, y + 4);
|
||||
btnJson.Size = new Size(200, 34);
|
||||
btnJson.Click += (s, e) => GenerateOllamaJson();
|
||||
page.Controls.Add(btnJson);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// vLLM 탭 빌드
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void BuildVllmTab(TabPage page)
|
||||
{
|
||||
int y = 12;
|
||||
var desc = CreateLabel("vLLM은 OpenAI 호환 API 서버입니다. API 키는 선택사항입니다.", 9f, FontStyle.Italic);
|
||||
desc.ForeColor = TextSecondary;
|
||||
desc.Location = new Point(12, y);
|
||||
desc.AutoSize = true;
|
||||
page.Controls.Add(desc);
|
||||
y += 30;
|
||||
|
||||
// Endpoint
|
||||
AddFieldPair(page, ref y, "Endpoint URL:", "http://localhost:8000",
|
||||
out _vllmEndpoint, out _vllmEncEndpoint, "암호화", AccentBlue, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_vllmEndpoint, _vllmEncEndpoint, "vLLM Endpoint");
|
||||
});
|
||||
|
||||
// Model
|
||||
AddFieldPair(page, ref y, "Model:", "meta-llama/Llama-3.1-8B-Instruct",
|
||||
out _vllmModel, out _vllmEncModel, "암호화", AccentBlue, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_vllmModel, _vllmEncModel, "vLLM Model");
|
||||
});
|
||||
|
||||
// API Key
|
||||
AddFieldPair(page, ref y, "API Key (선택):", "",
|
||||
out _vllmApiKey, out _vllmEncApiKey, "암호화", AccentBlue, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_vllmApiKey, _vllmEncApiKey, "vLLM API Key");
|
||||
});
|
||||
|
||||
var btnAll = CreateButton("⚡ 전체 암호화", AccentBlue);
|
||||
btnAll.Location = new Point(12, y + 4);
|
||||
btnAll.Size = new Size(130, 34);
|
||||
btnAll.Click += (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_vllmEndpoint, _vllmEncEndpoint, "vLLM Endpoint");
|
||||
EncryptAndShow(_vllmModel, _vllmEncModel, "vLLM Model");
|
||||
if (!string.IsNullOrEmpty(_vllmApiKey.Text.Trim()))
|
||||
EncryptAndShow(_vllmApiKey, _vllmEncApiKey, "vLLM API Key");
|
||||
AppendLog("[vLLM] 전체 암호화 완료 ✔", AccentGreen);
|
||||
};
|
||||
page.Controls.Add(btnAll);
|
||||
|
||||
var btnJson = CreateButton("📋 settings.json 복사", AccentBlue);
|
||||
btnJson.Location = new Point(150, y + 4);
|
||||
btnJson.Size = new Size(200, 34);
|
||||
btnJson.Click += (s, e) => GenerateVllmJson();
|
||||
page.Controls.Add(btnJson);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// Gemini 탭 빌드
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void BuildGeminiTab(TabPage page)
|
||||
{
|
||||
int y = 12;
|
||||
var desc = CreateLabel("Google Gemini API를 사용합니다. API Key가 필수입니다.", 9f, FontStyle.Italic);
|
||||
desc.ForeColor = TextSecondary;
|
||||
desc.Location = new Point(12, y);
|
||||
desc.AutoSize = true;
|
||||
page.Controls.Add(desc);
|
||||
y += 30;
|
||||
|
||||
// API Key
|
||||
AddFieldPair(page, ref y, "API Key (필수):", "",
|
||||
out _geminiApiKey, out _geminiEncApiKey, "암호화", AccentOrange, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_geminiApiKey, _geminiEncApiKey, "Gemini API Key");
|
||||
});
|
||||
|
||||
// Model
|
||||
AddFieldPair(page, ref y, "Model:", "gemini-2.0-flash",
|
||||
out _geminiModel, out _geminiEncModel, "암호화", AccentOrange, (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_geminiModel, _geminiEncModel, "Gemini Model");
|
||||
});
|
||||
|
||||
var btnAll = CreateButton("⚡ 전체 암호화", AccentOrange);
|
||||
btnAll.Location = new Point(12, y + 4);
|
||||
btnAll.Size = new Size(130, 34);
|
||||
btnAll.Click += (s, e) =>
|
||||
{
|
||||
EncryptAndShow(_geminiApiKey, _geminiEncApiKey, "Gemini API Key");
|
||||
EncryptAndShow(_geminiModel, _geminiEncModel, "Gemini Model");
|
||||
AppendLog("[Gemini] 전체 암호화 완료 ✔", AccentGreen);
|
||||
};
|
||||
page.Controls.Add(btnAll);
|
||||
|
||||
var btnJson = CreateButton("📋 settings.json 복사", AccentOrange);
|
||||
btnJson.Location = new Point(150, y + 4);
|
||||
btnJson.Size = new Size(200, 34);
|
||||
btnJson.Click += (s, e) => GenerateGeminiJson();
|
||||
page.Controls.Add(btnJson);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// 필드 쌍 헬퍼 (라벨 + 입력 + 암호화 버튼 + 결과)
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// 복호화 검증 탭
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
private void BuildToolsTab(TabPage page)
|
||||
{
|
||||
int y = 12;
|
||||
var desc = CreateLabel("암호화된 Base64 값을 붙여넣고 원문을 확인합니다.", 9f, FontStyle.Italic);
|
||||
desc.ForeColor = TextSecondary;
|
||||
desc.Location = new Point(12, y);
|
||||
desc.AutoSize = true;
|
||||
page.Controls.Add(desc);
|
||||
y += 30;
|
||||
|
||||
var inputLabel = CreateLabel("암호화된 값 (Base64):", 9f, FontStyle.Regular);
|
||||
inputLabel.Location = new Point(12, y);
|
||||
inputLabel.AutoSize = true;
|
||||
page.Controls.Add(inputLabel);
|
||||
y += 20;
|
||||
|
||||
_decryptInput = CreateTextBox();
|
||||
_decryptInput.Location = new Point(12, y);
|
||||
_decryptInput.Size = new Size(440, 28);
|
||||
page.Controls.Add(_decryptInput);
|
||||
|
||||
var btnDecrypt = CreateButton("복호화", AccentBlue);
|
||||
btnDecrypt.Location = new Point(460, y - 1);
|
||||
btnDecrypt.Size = new Size(80, 30);
|
||||
btnDecrypt.Click += BtnDecrypt_Click;
|
||||
page.Controls.Add(btnDecrypt);
|
||||
|
||||
var btnVerify = CreateButton("왕복 검증", Color.FromArgb(130, 100, 220));
|
||||
btnVerify.Location = new Point(546, y - 1);
|
||||
btnVerify.Size = new Size(90, 30);
|
||||
btnVerify.Click += BtnVerify_Click;
|
||||
page.Controls.Add(btnVerify);
|
||||
y += 40;
|
||||
|
||||
var resultLabel = CreateLabel("복호화 결과:", 9f, FontStyle.Regular);
|
||||
resultLabel.Location = new Point(12, y);
|
||||
resultLabel.AutoSize = true;
|
||||
page.Controls.Add(resultLabel);
|
||||
y += 20;
|
||||
|
||||
_decryptResult = CreateTextBox();
|
||||
_decryptResult.Location = new Point(12, y);
|
||||
_decryptResult.Size = new Size(624, 28);
|
||||
_decryptResult.ReadOnly = true;
|
||||
page.Controls.Add(_decryptResult);
|
||||
}
|
||||
|
||||
private void AddFieldPair(TabPage page, ref int y,
|
||||
string labelText, string placeholder,
|
||||
out TextBox input, out TextBox encrypted,
|
||||
string buttonText, Color accentColor, EventHandler onClick)
|
||||
{
|
||||
var lbl = CreateLabel(labelText, 9f, FontStyle.Regular);
|
||||
lbl.Location = new Point(12, y);
|
||||
lbl.AutoSize = true;
|
||||
page.Controls.Add(lbl);
|
||||
y += 22;
|
||||
|
||||
input = CreateTextBox();
|
||||
input.Location = new Point(12, y);
|
||||
input.Size = new Size(310, 28);
|
||||
input.PlaceholderText = placeholder;
|
||||
if (!string.IsNullOrEmpty(placeholder) && placeholder.StartsWith("http"))
|
||||
input.Text = placeholder;
|
||||
page.Controls.Add(input);
|
||||
|
||||
var btn = CreateButton(buttonText, accentColor);
|
||||
btn.Location = new Point(328, y - 1);
|
||||
btn.Size = new Size(70, 30);
|
||||
btn.Click += onClick;
|
||||
page.Controls.Add(btn);
|
||||
|
||||
encrypted = CreateTextBox();
|
||||
encrypted.Location = new Point(404, y);
|
||||
encrypted.Size = new Size(238, 28);
|
||||
encrypted.ReadOnly = true;
|
||||
encrypted.PlaceholderText = "암호화 결과";
|
||||
page.Controls.Add(encrypted);
|
||||
|
||||
// 복사 버튼
|
||||
var btnCopy = CreateCopyButton(encrypted);
|
||||
btnCopy.Location = new Point(646, y);
|
||||
btnCopy.Size = new Size(28, 28);
|
||||
page.Controls.Add(btnCopy);
|
||||
|
||||
y += 40;
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// 암호화 실행
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void EncryptAndShow(TextBox input, TextBox output, string fieldName)
|
||||
{
|
||||
var text = input.Text.Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
AppendLog($"[{fieldName}] 입력이 비어 있습니다.", AccentRed);
|
||||
return;
|
||||
}
|
||||
var enc = CryptoHelper.Encrypt(text);
|
||||
output.Text = enc;
|
||||
AppendLog($"[{fieldName}] 암호화 완료 ✔", AccentGreen);
|
||||
AppendLog($" 원본: {text}", TextSecondary);
|
||||
AppendLog($" 결과: {enc}", TextPrimary);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// 복호화
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void BtnDecrypt_Click(object? sender, EventArgs e)
|
||||
{
|
||||
var b64 = _decryptInput.Text.Trim();
|
||||
if (string.IsNullOrEmpty(b64))
|
||||
{
|
||||
AppendLog("[복호화] 입력이 비어 있습니다.", AccentRed);
|
||||
return;
|
||||
}
|
||||
var (success, result) = CryptoHelper.Decrypt(b64);
|
||||
_decryptResult.Text = result;
|
||||
if (success)
|
||||
{
|
||||
_decryptResult.ForeColor = AccentGreen;
|
||||
AppendLog($"[복호화] 성공 ✔ → {result}", AccentGreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
_decryptResult.ForeColor = AccentRed;
|
||||
AppendLog($"[복호화] 실패 ✘ — {result}", AccentRed);
|
||||
}
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// 왕복 검증
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void BtnVerify_Click(object? sender, EventArgs e)
|
||||
{
|
||||
var b64 = _decryptInput.Text.Trim();
|
||||
string testText;
|
||||
if (string.IsNullOrEmpty(b64))
|
||||
{
|
||||
testText = "test-api-key-12345";
|
||||
AppendLog("[왕복 검증] 테스트 문자열 사용: " + testText, TextSecondary);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 이미 암호화된 값이면 복호화 → 재암호화 → 재복호화
|
||||
var (ok, dec) = CryptoHelper.Decrypt(b64);
|
||||
if (ok) testText = dec;
|
||||
else testText = b64; // 평문으로 간주
|
||||
}
|
||||
|
||||
var encrypted = CryptoHelper.Encrypt(testText);
|
||||
var (success, decrypted) = CryptoHelper.Decrypt(encrypted);
|
||||
var match = success && testText == decrypted;
|
||||
|
||||
_decryptResult.Text = match ? $"✅ 일치 — {decrypted}" : $"❌ 불일치";
|
||||
_decryptResult.ForeColor = match ? AccentGreen : AccentRed;
|
||||
|
||||
AppendLog("[왕복 검증]", AccentBlue);
|
||||
AppendLog($" 원본: {testText}", TextSecondary);
|
||||
AppendLog($" 암호화: {encrypted}", TextSecondary);
|
||||
AppendLog($" 복호화: {decrypted}", TextSecondary);
|
||||
AppendLog($" 결과: {(match ? "✅ 성공" : "❌ 실패")}", match ? AccentGreen : AccentRed);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// JSON 생성 (서비스별)
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void GenerateOllamaJson()
|
||||
{
|
||||
var endpoint = _ollamaEncEndpoint.Text;
|
||||
var model = _ollamaEncModel.Text;
|
||||
|
||||
if (string.IsNullOrEmpty(endpoint)) endpoint = CryptoHelper.Encrypt(_ollamaEndpoint.Text);
|
||||
if (string.IsNullOrEmpty(model)) model = CryptoHelper.Encrypt(_ollamaModel.Text);
|
||||
|
||||
var json = JsonSerializer.Serialize(new
|
||||
{
|
||||
llm = new
|
||||
{
|
||||
service = "ollama",
|
||||
encryptedEndpoint = endpoint,
|
||||
encryptedModel = model,
|
||||
streaming = true,
|
||||
maxContextTokens = 4096,
|
||||
temperature = 0.7
|
||||
}
|
||||
}, new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
CopyToClipboard(json, "Ollama");
|
||||
}
|
||||
|
||||
private void GenerateVllmJson()
|
||||
{
|
||||
var endpoint = _vllmEncEndpoint.Text;
|
||||
var model = _vllmEncModel.Text;
|
||||
var apiKey = _vllmEncApiKey.Text;
|
||||
|
||||
if (string.IsNullOrEmpty(endpoint)) endpoint = CryptoHelper.Encrypt(_vllmEndpoint.Text);
|
||||
if (string.IsNullOrEmpty(model)) model = CryptoHelper.Encrypt(_vllmModel.Text);
|
||||
if (string.IsNullOrEmpty(apiKey) && !string.IsNullOrEmpty(_vllmApiKey.Text))
|
||||
apiKey = CryptoHelper.Encrypt(_vllmApiKey.Text);
|
||||
|
||||
var obj = new Dictionary<string, object>
|
||||
{
|
||||
["llm"] = new Dictionary<string, object>
|
||||
{
|
||||
["service"] = "vllm",
|
||||
["encryptedEndpoint"] = endpoint,
|
||||
["encryptedModel"] = model,
|
||||
["encryptedApiKey"] = apiKey ?? "",
|
||||
["streaming"] = true,
|
||||
["maxContextTokens"] = 4096,
|
||||
["temperature"] = 0.7
|
||||
}
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
|
||||
CopyToClipboard(json, "vLLM");
|
||||
}
|
||||
|
||||
private void GenerateGeminiJson()
|
||||
{
|
||||
var apiKey = _geminiEncApiKey.Text;
|
||||
var model = _geminiEncModel.Text;
|
||||
|
||||
if (string.IsNullOrEmpty(apiKey))
|
||||
{
|
||||
if (string.IsNullOrEmpty(_geminiApiKey.Text))
|
||||
{
|
||||
AppendLog("[Gemini] API Key는 필수입니다!", AccentRed);
|
||||
return;
|
||||
}
|
||||
apiKey = CryptoHelper.Encrypt(_geminiApiKey.Text);
|
||||
}
|
||||
if (string.IsNullOrEmpty(model)) model = CryptoHelper.Encrypt(_geminiModel.Text);
|
||||
|
||||
var json = JsonSerializer.Serialize(new
|
||||
{
|
||||
llm = new
|
||||
{
|
||||
service = "gemini",
|
||||
encryptedApiKey = apiKey,
|
||||
encryptedModel = model,
|
||||
streaming = true,
|
||||
maxContextTokens = 8192,
|
||||
temperature = 0.7
|
||||
}
|
||||
}, new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
CopyToClipboard(json, "Gemini");
|
||||
}
|
||||
|
||||
private void CopyToClipboard(string json, string serviceName)
|
||||
{
|
||||
Clipboard.SetText(json);
|
||||
AppendLog($"[{serviceName}] settings.json 설정값이 클립보드에 복사되었습니다! 📋", AccentGreen);
|
||||
AppendLog(json, TextSecondary);
|
||||
AppendLog("", TextSecondary);
|
||||
|
||||
// 짧은 알림
|
||||
var tip = new ToolTip();
|
||||
tip.Show($"{serviceName} 설정이 클립보드에 복사됨!", this, Width / 2 - 100, Height - 60, 2000);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// 출력 로그
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private void AppendLog(string text, Color color)
|
||||
{
|
||||
_outputBox.SelectionStart = _outputBox.TextLength;
|
||||
_outputBox.SelectionLength = 0;
|
||||
_outputBox.SelectionColor = color;
|
||||
_outputBox.AppendText(text + Environment.NewLine);
|
||||
_outputBox.ScrollToCaret();
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
// UI 팩토리 헬퍼
|
||||
// ═════════════════════════════════════════════════════════════════
|
||||
private static Label CreateLabel(string text, float fontSize, FontStyle style)
|
||||
{
|
||||
return new Label
|
||||
{
|
||||
Text = text,
|
||||
ForeColor = TextPrimary,
|
||||
Font = new Font("Segoe UI", fontSize, style),
|
||||
AutoSize = true
|
||||
};
|
||||
}
|
||||
|
||||
private static TextBox CreateTextBox()
|
||||
{
|
||||
return new TextBox
|
||||
{
|
||||
BackColor = BgInput,
|
||||
ForeColor = TextPrimary,
|
||||
Font = new Font("Segoe UI", 10f),
|
||||
BorderStyle = BorderStyle.FixedSingle
|
||||
};
|
||||
}
|
||||
|
||||
private Button CreateButton(string text, Color bgColor)
|
||||
{
|
||||
var btn = new Button
|
||||
{
|
||||
Text = text,
|
||||
BackColor = bgColor,
|
||||
ForeColor = Color.White,
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Font = new Font("Segoe UI", 9f, FontStyle.Bold),
|
||||
Cursor = Cursors.Hand
|
||||
};
|
||||
btn.FlatAppearance.BorderSize = 0;
|
||||
btn.FlatAppearance.MouseOverBackColor = ControlPaint.Light(bgColor, 0.15f);
|
||||
btn.FlatAppearance.MouseDownBackColor = ControlPaint.Dark(bgColor, 0.1f);
|
||||
return btn;
|
||||
}
|
||||
|
||||
private Button CreateCopyButton(TextBox target)
|
||||
{
|
||||
var btn = new Button
|
||||
{
|
||||
Text = "📋",
|
||||
BackColor = BgCard,
|
||||
ForeColor = TextPrimary,
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Font = new Font("Segoe UI", 9f),
|
||||
Cursor = Cursors.Hand
|
||||
};
|
||||
btn.FlatAppearance.BorderSize = 1;
|
||||
btn.FlatAppearance.BorderColor = BorderColor;
|
||||
btn.Click += (s, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(target.Text))
|
||||
{
|
||||
Clipboard.SetText(target.Text);
|
||||
var tip = new ToolTip();
|
||||
tip.Show("복사됨!", btn, 0, -20, 1200);
|
||||
}
|
||||
};
|
||||
return btn;
|
||||
}
|
||||
|
||||
private TabPage CreateTabPage(string title, Color accentColor)
|
||||
{
|
||||
var page = new TabPage(title)
|
||||
{
|
||||
BackColor = BgPanel,
|
||||
ForeColor = TextPrimary,
|
||||
Padding = new Padding(4)
|
||||
};
|
||||
page.Tag = accentColor; // 탭 드로잉에 사용
|
||||
return page;
|
||||
}
|
||||
|
||||
private void DrawTab(object? sender, DrawItemEventArgs e)
|
||||
{
|
||||
if (sender is not TabControl tabs) return;
|
||||
var page = tabs.TabPages[e.Index];
|
||||
var isSelected = e.Index == tabs.SelectedIndex;
|
||||
var accent = page.Tag is Color c ? c : AccentBlue;
|
||||
|
||||
// 배경
|
||||
var bgColor = isSelected ? BgCard : BgPanel;
|
||||
using var bgBrush = new SolidBrush(bgColor);
|
||||
e.Graphics.FillRectangle(bgBrush, e.Bounds);
|
||||
|
||||
// 선택된 탭 하단 악센트 라인
|
||||
if (isSelected)
|
||||
{
|
||||
using var lineBrush = new SolidBrush(accent);
|
||||
e.Graphics.FillRectangle(lineBrush,
|
||||
new Rectangle(e.Bounds.Left, e.Bounds.Bottom - 3, e.Bounds.Width, 3));
|
||||
}
|
||||
|
||||
// 텍스트
|
||||
var textColor = isSelected ? TextPrimary : TextSecondary;
|
||||
using var textBrush = new SolidBrush(textColor);
|
||||
var textFont = isSelected
|
||||
? new Font("Segoe UI", 10f, FontStyle.Bold)
|
||||
: new Font("Segoe UI", 9.5f, FontStyle.Regular);
|
||||
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
e.Graphics.DrawString(page.Text, textFont, textBrush, e.Bounds, sf);
|
||||
textFont.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user