diff --git a/app/Http/Controllers/System/AiConfigController.php b/app/Http/Controllers/System/AiConfigController.php
index b4a6f932..d927321e 100644
--- a/app/Http/Controllers/System/AiConfigController.php
+++ b/app/Http/Controllers/System/AiConfigController.php
@@ -34,10 +34,18 @@ public function index(Request $request): View|Response
->orderBy('name')
->get();
+ // API 서비스 설정 (notion)
+ $apiServiceConfigs = AiConfig::whereIn('provider', AiConfig::API_SERVICE_PROVIDERS)
+ ->orderBy('provider')
+ ->orderByDesc('is_active')
+ ->orderBy('name')
+ ->get();
+
return view('system.ai-config.index', [
'configs' => $aiConfigs,
'aiConfigs' => $aiConfigs,
'storageConfigs' => $storageConfigs,
+ 'apiServiceConfigs' => $apiServiceConfigs,
]);
}
diff --git a/app/Models/System/AiConfig.php b/app/Models/System/AiConfig.php
index 66f575ac..e47bab36 100644
--- a/app/Models/System/AiConfig.php
+++ b/app/Models/System/AiConfig.php
@@ -69,13 +69,18 @@ class AiConfig extends Model
/**
* AI Provider 목록 (GCS 제외)
*/
- public const AI_PROVIDERS = ['gemini', 'claude', 'openai', 'notion'];
+ public const AI_PROVIDERS = ['gemini', 'claude', 'openai'];
/**
* 스토리지 Provider 목록
*/
public const STORAGE_PROVIDERS = ['gcs'];
+ /**
+ * API 서비스 Provider 목록
+ */
+ public const API_SERVICE_PROVIDERS = ['notion'];
+
/**
* 활성화된 Gemini 설정 조회
*/
diff --git a/resources/views/system/ai-config/index.blade.php b/resources/views/system/ai-config/index.blade.php
index f5fa2de9..fcc17313 100644
--- a/resources/views/system/ai-config/index.blade.php
+++ b/resources/views/system/ai-config/index.blade.php
@@ -19,6 +19,7 @@
.provider-gemini { background: #e8f0fe; color: #1a73e8; }
.provider-claude { background: #fef3e8; color: #d97706; }
.provider-openai { background: #e8f8e8; color: #16a34a; }
+ .provider-notion { background: #f3f0ff; color: #7c3aed; }
.status-badge {
display: inline-flex;
@@ -239,6 +240,79 @@
+
+
+
+
+
+
+
API 서비스 설정 (Notion)
+
+
+
+
+
+ @forelse($apiServiceConfigs as $config)
+
+
+
+
+
{{ $config->name }}
+ Notion
+
+ {{ $config->status_label }}
+
+
+
+
API 버전: {{ $config->model }}
+
API 키: {{ $config->masked_api_key }}
+ @if($config->description)
+
설명: {{ $config->description }}
+ @endif
+
+
+
+
+
+
+
+
+
+ @empty
+
+
+
등록된 Notion 설정이 없습니다.
+
'Notion 설정 추가' 버튼을 클릭하여 Notion API를 등록하세요.
+
+ @endforelse
+
+
+
+
+
Notion API 사용 안내
+
+ - Notion 검색 기능(추가기능 > Notion 검색)에서 사용됩니다.
+ - Notion Integrations에서 Internal Integration을 생성하고 API 키를 발급받으세요.
+ - 검색할 페이지/데이터베이스에 Integration을 연결(Share)해야 합니다.
+ - Gemini AI 설정이 활성화되어 있어야 검색어 정제 및 AI 답변이 동작합니다.
+
+
+
@@ -312,6 +386,60 @@
+
+
+
+
+
Notion 설정 추가
+
+
+
+
+
+
+
@@ -912,6 +1040,97 @@ function toggleGcsAuthType(type) {
}
}
+ // === Notion 설정 관련 함수들 ===
+
+ // Notion 모달 열기
+ window.openNotionModal = function(config) {
+ const modal = document.getElementById('notion-modal');
+ const title = document.getElementById('notion-modal-title');
+
+ if (config) {
+ title.textContent = 'Notion 설정 수정';
+ document.getElementById('notion-config-id').value = config.id;
+ document.getElementById('notion-name').value = config.name;
+ document.getElementById('notion-api-key').value = config.api_key || '';
+ document.getElementById('notion-api-version').value = config.model || '';
+ document.getElementById('notion-description').value = config.description || '';
+ document.getElementById('notion-is-active').checked = config.is_active;
+ } else {
+ title.textContent = 'Notion 설정 추가';
+ document.getElementById('notion-form').reset();
+ document.getElementById('notion-config-id').value = '';
+ }
+
+ modal.classList.remove('hidden');
+ };
+
+ // Notion 모달 닫기
+ window.closeNotionModal = function() {
+ document.getElementById('notion-modal').classList.add('hidden');
+ };
+
+ // Notion 수정
+ window.editNotionConfig = function(btn) {
+ try {
+ const config = JSON.parse(btn.dataset.config);
+ window.openNotionModal(config);
+ } catch (e) {
+ console.error('Config parse error:', e);
+ showToast('설정 데이터를 불러올 수 없습니다.', 'error');
+ }
+ };
+
+ // Notion 폼 제출
+ async function handleNotionFormSubmit(e) {
+ e.preventDefault();
+
+ const id = document.getElementById('notion-config-id').value;
+ const apiKey = document.getElementById('notion-api-key').value;
+
+ if (!apiKey) {
+ showToast('API 키를 입력해주세요.', 'error');
+ return;
+ }
+
+ const data = {
+ provider: 'notion',
+ name: document.getElementById('notion-name').value,
+ api_key: apiKey,
+ model: document.getElementById('notion-api-version').value || '2025-09-03',
+ description: document.getElementById('notion-description').value || null,
+ is_active: document.getElementById('notion-is-active').checked,
+ options: {},
+ };
+
+ try {
+ const url = id
+ ? `{{ url('system/ai-config') }}/${id}`
+ : '{{ route("system.ai-config.store") }}';
+ const method = id ? 'PUT' : 'POST';
+
+ const response = await fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-TOKEN': csrfToken
+ },
+ body: JSON.stringify(data)
+ });
+
+ const result = await response.json();
+
+ if (result.ok) {
+ showToast(result.message, 'success');
+ window.closeNotionModal();
+ location.reload();
+ } else {
+ showToast(result.message || '저장 실패', 'error');
+ }
+ } catch (error) {
+ showToast('저장 중 오류가 발생했습니다.', 'error');
+ }
+ }
+
// DOM 로드 후 이벤트 리스너 등록
document.addEventListener('DOMContentLoaded', function() {
// 페이지 로드 시 모달 강제 닫기
@@ -923,6 +1142,10 @@ function toggleGcsAuthType(type) {
if (gcsModal) {
gcsModal.classList.add('hidden');
}
+ const notionModal = document.getElementById('notion-modal');
+ if (notionModal) {
+ notionModal.classList.add('hidden');
+ }
// Provider 변경 시 기본 모델 업데이트 및 UI 전환
const providerEl = document.getElementById('config-provider');
@@ -969,6 +1192,12 @@ function toggleGcsAuthType(type) {
});
}
+ // Notion 폼 제출
+ const notionFormEl = document.getElementById('notion-form');
+ if (notionFormEl) {
+ notionFormEl.addEventListener('submit', handleNotionFormSubmit);
+ }
+
// 모달 외부 클릭 시 닫지 않음 (의도치 않은 닫힘 방지)
// 닫기 버튼이나 취소 버튼으로만 닫을 수 있음
});