|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
const uploadButton = document.getElementById('uploadButton'); |
|
|
const uploadSuccess = document.getElementById('uploadSuccess'); |
|
|
const uploadSuccessMessage = document.getElementById('uploadSuccessMessage'); |
|
|
const uploadError = document.getElementById('uploadError'); |
|
|
const uploadErrorMessage = document.getElementById('uploadErrorMessage'); |
|
|
const refreshStatus = document.getElementById('refreshStatus'); |
|
|
const databaseStats = document.getElementById('databaseStats'); |
|
|
const documentsContainer = document.getElementById('documentsContainer'); |
|
|
|
|
|
|
|
|
Dropzone.autoDiscover = false; |
|
|
|
|
|
const documentUploadDropzone = new Dropzone("#documentUploadForm", { |
|
|
url: "/api/upload", |
|
|
maxFilesize: 10, |
|
|
acceptedFiles: ".txt,.md,.pdf,.docx,.csv", |
|
|
addRemoveLinks: true, |
|
|
dictDefaultMessage: "ํ์ผ์ ๋์ด๋ค ๋๊ฑฐ๋ ํด๋ฆญํ์ฌ ์ ํํ์ธ์", |
|
|
dictRemoveFile: "์ ๊ฑฐ", |
|
|
dictCancelUpload: "์
๋ก๋ ์ทจ์", |
|
|
dictUploadCanceled: "์
๋ก๋ ์ทจ์๋จ", |
|
|
dictFileTooBig: "ํ์ผ์ด ๋๋ฌด ํฝ๋๋ค ({{filesize}}MB). ์ต๋ ํ์ผ ํฌ๊ธฐ: {{maxFilesize}}MB.", |
|
|
dictInvalidFileType: "์ด ํ์์ ํ์ผ์ ์
๋ก๋ํ ์ ์์ต๋๋ค.", |
|
|
autoProcessQueue: false, |
|
|
maxFiles: 1 |
|
|
}); |
|
|
|
|
|
|
|
|
documentUploadDropzone.on("addedfile", function(file) { |
|
|
uploadButton.disabled = false; |
|
|
hideAlerts(); |
|
|
}); |
|
|
|
|
|
|
|
|
documentUploadDropzone.on("removedfile", function(file) { |
|
|
if (documentUploadDropzone.files.length === 0) { |
|
|
uploadButton.disabled = true; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
uploadButton.addEventListener('click', function() { |
|
|
if (documentUploadDropzone.files.length > 0) { |
|
|
uploadButton.disabled = true; |
|
|
uploadButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status"></span>์
๋ก๋ ์ค...'; |
|
|
documentUploadDropzone.processQueue(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
documentUploadDropzone.on("success", function(file, response) { |
|
|
uploadButton.innerHTML = '<i class="fas fa-upload me-2"></i>์
๋ก๋'; |
|
|
uploadButton.disabled = true; |
|
|
|
|
|
|
|
|
uploadSuccess.classList.remove('d-none'); |
|
|
uploadSuccessMessage.textContent = response.message || "๋ฌธ์๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์
๋ก๋๋์์ต๋๋ค."; |
|
|
|
|
|
|
|
|
documentUploadDropzone.removeFile(file); |
|
|
|
|
|
|
|
|
setTimeout(fetchDatabaseStats, 1000); |
|
|
setTimeout(fetchDocuments, 1000); |
|
|
}); |
|
|
|
|
|
|
|
|
documentUploadDropzone.on("error", function(file, errorMessage, xhr) { |
|
|
uploadButton.innerHTML = '<i class="fas fa-upload me-2"></i>์
๋ก๋'; |
|
|
uploadButton.disabled = false; |
|
|
|
|
|
|
|
|
uploadError.classList.remove('d-none'); |
|
|
|
|
|
if (typeof errorMessage === 'object' && errorMessage.error) { |
|
|
uploadErrorMessage.textContent = errorMessage.error; |
|
|
} else if (typeof errorMessage === 'string') { |
|
|
uploadErrorMessage.textContent = errorMessage; |
|
|
} else { |
|
|
uploadErrorMessage.textContent = "์
๋ก๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
refreshStatus.addEventListener('click', function() { |
|
|
fetchDatabaseStats(); |
|
|
fetchDocuments(); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hideAlerts() { |
|
|
uploadSuccess.classList.add('d-none'); |
|
|
uploadError.classList.add('d-none'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchDatabaseStats() { |
|
|
try { |
|
|
databaseStats.innerHTML = ` |
|
|
<div class="d-flex justify-content-center align-items-center" style="height: 100px;"> |
|
|
<div class="spinner-border text-primary" role="status"> |
|
|
<span class="visually-hidden">Loading...</span> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
const response = await fetch('/api/documents'); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error('API ์์ฒญ ์คํจ'); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
if (data.enabled === false) { |
|
|
databaseStats.innerHTML = ` |
|
|
<div class="alert alert-warning mb-0"> |
|
|
<i class="fas fa-exclamation-triangle me-2"></i>์บ์๊ฐ ํ์ฑํ๋์ง ์์์ต๋๋ค. |
|
|
</div> |
|
|
`; |
|
|
return; |
|
|
} |
|
|
|
|
|
const stats = data.stats || {}; |
|
|
|
|
|
databaseStats.innerHTML = ` |
|
|
<div class="row text-center"> |
|
|
<div class="col-6 border-end"> |
|
|
<h3 class="mb-0">${stats.size || 0}</h3> |
|
|
<p class="text-muted mb-0">์บ์ ํญ๋ชฉ</p> |
|
|
</div> |
|
|
<div class="col-6"> |
|
|
<h3 class="mb-0">${stats.max_size || 0}</h3> |
|
|
<p class="text-muted mb-0">์ต๋ ํญ๋ชฉ ์</p> |
|
|
</div> |
|
|
</div> |
|
|
<hr> |
|
|
<div class="text-center"> |
|
|
<p class="mb-0">์บ์ TTL: ${stats.ttl || 0}์ด</p> |
|
|
<p class="mb-2">์ต๋ ํญ๋ชฉ ๋์ด: ${(stats.oldest_item_age || 0).toFixed(1)}์ด</p> |
|
|
</div> |
|
|
<div class="d-grid"> |
|
|
<button class="btn btn-sm btn-outline-primary" id="clearCache">์บ์ ๋น์ฐ๊ธฐ</button> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
document.getElementById('clearCache').addEventListener('click', clearCache); |
|
|
|
|
|
} catch (err) { |
|
|
console.error('์ง์๋ฒ ์ด์ค ์ํ ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', err); |
|
|
databaseStats.innerHTML = ` |
|
|
<div class="alert alert-danger mb-0"> |
|
|
<i class="fas fa-exclamation-circle me-2"></i>์ง์๋ฒ ์ด์ค ์ํ๋ฅผ ๊ฐ์ ธ์ค๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchDocuments() { |
|
|
try { |
|
|
documentsContainer.innerHTML = ` |
|
|
<div class="text-center p-4"> |
|
|
<div class="spinner-border text-primary" role="status"> |
|
|
<span class="visually-hidden">Loading...</span> |
|
|
</div> |
|
|
<p class="mt-2">๋ฌธ์ ๋ชฉ๋ก์ ๋ถ๋ฌ์ค๋ ์ค...</p> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
const response = await fetch('/api/documents'); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error('API ์์ฒญ ์คํจ'); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
const documents = data.documents || []; |
|
|
|
|
|
if (documents.length === 0) { |
|
|
documentsContainer.innerHTML = ` |
|
|
<div class="text-center p-4"> |
|
|
<i class="fas fa-file-alt fa-3x mb-3 text-muted"></i> |
|
|
<p>์ง์๋ฒ ์ด์ค์ ๋ฑ๋ก๋ ๋ฌธ์๊ฐ ์์ต๋๋ค.</p> |
|
|
<p class="text-muted small">์ผ์ชฝ ํจ๋์์ ๋ฌธ์๋ฅผ ์
๋ก๋ํ์ธ์.</p> |
|
|
</div> |
|
|
`; |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const documentsList = document.createElement('div'); |
|
|
documentsList.className = 'list-group list-group-flush'; |
|
|
|
|
|
documents.forEach(doc => { |
|
|
const docItem = createDocumentItem(doc); |
|
|
documentsList.appendChild(docItem); |
|
|
}); |
|
|
|
|
|
documentsContainer.innerHTML = ''; |
|
|
documentsContainer.appendChild(documentsList); |
|
|
|
|
|
} catch (err) { |
|
|
console.error('๋ฌธ์ ๋ชฉ๋ก ๊ฐ์ ธ์ค๊ธฐ ์คํจ:', err); |
|
|
documentsContainer.innerHTML = ` |
|
|
<div class="alert alert-danger m-3"> |
|
|
<i class="fas fa-exclamation-circle me-2"></i>๋ฌธ์ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createDocumentItem(doc) { |
|
|
const template = document.getElementById('documentItemTemplate'); |
|
|
const docNode = template.content.cloneNode(true); |
|
|
|
|
|
docNode.querySelector('.document-name').textContent = doc.filename || doc.source || 'Unnamed Document'; |
|
|
docNode.querySelector('.document-chunks').textContent = doc.chunks || 0; |
|
|
docNode.querySelector('.document-type').textContent = doc.filetype || 'UNKNOWN'; |
|
|
|
|
|
return docNode.firstElementChild; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function clearCache() { |
|
|
try { |
|
|
if (!confirm('์ ๋ง ์บ์๋ฅผ ๋น์ฐ์๊ฒ ์ต๋๊น?')) { |
|
|
return; |
|
|
} |
|
|
|
|
|
const response = await fetch('/api/cache/clear', { |
|
|
method: 'POST' |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error('API ์์ฒญ ์คํจ'); |
|
|
} |
|
|
|
|
|
alert('์บ์๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋น์์ก์ต๋๋ค.'); |
|
|
fetchDatabaseStats(); |
|
|
|
|
|
} catch (err) { |
|
|
console.error('์บ์ ๋น์ฐ๊ธฐ ์คํจ:', err); |
|
|
alert('์บ์ ๋น์ฐ๊ธฐ ์คํจ: ' + err.message); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fetchDatabaseStats(); |
|
|
fetchDocuments(); |
|
|
}); |
|
|
|