@@ -887,6 +928,117 @@
function closeUploadModal() {
uploadModal.classList.remove('active');
}
+
+ // ---- Full-Text Search (Meilisearch) ----
+ let _searchDebounceTimer = null;
+ let _searchCurrentPage = 1;
+
+ function debounceSearch(value) {
+ clearTimeout(_searchDebounceTimer);
+ if (!value || value.trim().length < 2) {
+ clearFullTextSearch();
+ return;
+ }
+ _searchDebounceTimer = setTimeout(() => {
+ _searchCurrentPage = 1;
+ runFullTextSearch();
+ }, 400);
+ }
+
+ function runFullTextSearch(page) {
+ const input = document.getElementById('fulltext-search');
+ const q = input ? input.value.trim() : '';
+ if (!q) { clearFullTextSearch(); return; }
+ if (page) _searchCurrentPage = page;
+
+ const panel = document.getElementById('search-results-panel');
+ const list = document.getElementById('search-results-list');
+ const summary = document.getElementById('search-results-summary');
+ const pagination = document.getElementById('search-results-pagination');
+
+ list.innerHTML = '
Searching…
';
+ summary.textContent = '';
+ pagination.innerHTML = '';
+ panel.style.display = 'block';
+
+ const params = new URLSearchParams({ q, page: _searchCurrentPage, per_page: 20 });
+ fetch(`/api/search?${params.toString()}`)
+ .then(r => {
+ if (!r.ok) throw new Error(`Search returned ${r.status}`);
+ return r.json();
+ })
+ .then(data => renderSearchResults(data, q))
+ .catch(err => {
+ list.innerHTML = `
Search unavailable: ${err.message}
`;
+ summary.textContent = '';
+ });
+ }
+
+ function renderSearchResults(data, q) {
+ const panel = document.getElementById('search-results-panel');
+ const list = document.getElementById('search-results-list');
+ const summary = document.getElementById('search-results-summary');
+ const pagination = document.getElementById('search-results-pagination');
+
+ const { results, total, page, pages } = data;
+ summary.textContent = `${total} result${total !== 1 ? 's' : ''} for "${q}"`;
+
+ if (!results || results.length === 0) {
+ list.innerHTML = '
No results found.
';
+ pagination.innerHTML = '';
+ return;
+ }
+
+ list.innerHTML = results.map(hit => {
+ const fmt = hit._formatted || {};
+ const title = fmt.document_title || hit.document_title || hit.original_filename || '(untitled)';
+ const filename = fmt.original_filename || hit.original_filename || '';
+ const snippet = fmt.ocr_text || '';
+ const tags = Array.isArray(hit.tags) ? hit.tags.join(', ') : (hit.tags || '');
+ const docType = hit.document_type || '';
+
+ return `
+
+
+
+
+
${title}
+ ${filename ? `
${filename}
` : ''}
+ ${docType ? `
${docType}` : ''}
+ ${tags ? `
${tags}` : ''}
+ ${snippet ? `
…${snippet}…
` : ''}
+
+
+
`;
+ }).join('');
+
+ // Pagination
+ if (pages > 1) {
+ const btns = [];
+ if (page > 1) {
+ btns.push(`
`);
+ }
+ btns.push(`
Page ${page} / ${pages}`);
+ if (page < pages) {
+ btns.push(`
`);
+ }
+ pagination.innerHTML = btns.join('');
+ } else {
+ pagination.innerHTML = '';
+ }
+ }
+
+ function clearFullTextSearch() {
+ const panel = document.getElementById('search-results-panel');
+ const input = document.getElementById('fulltext-search');
+ if (panel) panel.style.display = 'none';
+ if (input) input.value = '';
+ _searchCurrentPage = 1;
+ }
{% endblock %}
diff --git a/helm/docuelevate/.helmignore b/helm/docuelevate/.helmignore
new file mode 100644
index 00000000..cdb7f322
--- /dev/null
+++ b/helm/docuelevate/.helmignore
@@ -0,0 +1,10 @@
+# Patterns to ignore when building packages.
+.DS_Store
+.git
+.gitignore
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+.vscode
diff --git a/helm/docuelevate/Chart.yaml b/helm/docuelevate/Chart.yaml
new file mode 100644
index 00000000..7da29b15
--- /dev/null
+++ b/helm/docuelevate/Chart.yaml
@@ -0,0 +1,37 @@
+apiVersion: v2
+name: docuelevate
+description: >
+ DocuElevate — intelligent document processing with OCR, AI metadata
+ extraction, full-text search (Meilisearch), and multi-cloud storage.
+
+type: application
+
+# Chart version — bump on every chart change (independent of appVersion).
+version: 0.1.0
+
+# Application version — kept in sync with the VERSION file.
+appVersion: "0.54.0"
+
+keywords:
+ - document-management
+ - ocr
+ - ai
+ - meilisearch
+ - fastapi
+ - celery
+
+home: https://github.com/christianlouis/DocuElevate
+sources:
+ - https://github.com/christianlouis/DocuElevate
+
+maintainers:
+ - name: DocuElevate Contributors
+ url: https://github.com/christianlouis/DocuElevate
+
+dependencies:
+ # Bundled Redis (optional — disable and point to an external instance via
+ # externalRedis.url if you already have Redis in the cluster).
+ - name: redis
+ version: "20.x.x"
+ repository: "https://charts.bitnami.com/bitnami"
+ condition: redis.enabled
diff --git a/helm/docuelevate/templates/NOTES.txt b/helm/docuelevate/templates/NOTES.txt
new file mode 100644
index 00000000..57c14773
--- /dev/null
+++ b/helm/docuelevate/templates/NOTES.txt
@@ -0,0 +1,45 @@
+Thank you for installing {{ .Chart.Name }} {{ .Chart.Version }}!
+
+{{- if .Values.ingress.enabled }}
+The application will be available at:
+{{- range .Values.ingress.hosts }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}
+{{- end }}
+{{- else }}
+To access DocuElevate, run:
+
+ kubectl port-forward svc/{{ include "docuelevate.fullname" . }}-api {{ .Values.api.service.port }}:{{ .Values.api.service.port }} -n {{ .Release.Namespace }}
+
+Then open http://localhost:{{ .Values.api.service.port }}
+{{- end }}
+
+API docs are available at: