Skip to content

Commit 6a94423

Browse files
chore(release): rilascia versione 1.3.0
1 parent 60ab049 commit 6a94423

File tree

6 files changed

+107
-81
lines changed

6 files changed

+107
-81
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
## [1.3.0] - 2025-12-04
11+
12+
### Changed
13+
- Frontend semplificato: rimosso upload cartelle, ora richiede file ZIP per caricare multiple documenti
14+
- Rimossa dipendenza JSZip dal frontend (compressione ora gestita dall'utente)
15+
16+
### Added
17+
- Modale informativa quando si tenta di trascinare una cartella, con istruzioni per creare ZIP
18+
1019
## [1.2.3] - 2025-12-03
1120

1221
### Fixed

api/routes/upload.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ def run_zip_indexing(job_id: str, zip_path: Path, collection_dir: Path, collecti
364364
# Remove ZIP after extraction
365365
zip_path.unlink()
366366

367-
logger.info(f"[{job_id}] Extracted {len(extracted_files)} files from ZIP")
367+
logger.info(f"[{job_id}] Extracted {len(extracted_files)} files from ZIP: {extracted_files}")
368368
jobs[job_id]["message"] = f"Extracted {len(extracted_files)} files"
369369
jobs[job_id]["progress"] = 0.15
370370
jobs[job_id]["filename"] = f"{len(extracted_files)} files"

docker-compose.local.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
ragify:
3+
image: ragify:test
4+
ports:
5+
- "8080:8080" # API + Web UI
6+
- "6333:6333" # Qdrant (opzionale, per debug)
7+
environment:
8+
- TIKA_ENABLED=true
9+
volumes:
10+
- ragify_data:/data
11+
restart: unless-stopped
12+
13+
volumes:
14+
ragify_data:

frontend/index.html

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<title>Ragify - Control Board</title>
77
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
88
<link rel="apple-touch-icon" href="/static/favicon.svg">
9-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
109
<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
1110
<link rel="stylesheet" href="/static/style.css">
1211
</head>
@@ -144,9 +143,9 @@ <h2>Document Input</h2>
144143
@drop.prevent="handleDrop($event)"
145144
:class="{ 'dragover': dragOver }">
146145
<div class="dropzone-content">
147-
<p>Drop files here</p>
146+
<p>Drop files or ZIP here</p>
148147
<label class="btn btn-primary">
149-
Browse
148+
Select Files
150149
<input type="file" @change="handleFileSelect($event)" multiple hidden>
151150
</label>
152151
</div>
@@ -165,13 +164,6 @@ <h2>Document Input</h2>
165164
</button>
166165
</div>
167166

168-
<!-- Local Upload Progress (browser-side) -->
169-
<div class="upload-local-progress" x-show="uploadPhase">
170-
<div class="upload-phase-label" x-text="uploadPhase === 'zipping' ? 'Zipping files...' : uploadPhase === 'uploading' ? 'Uploading...' : ''"></div>
171-
<div class="progress-bar">
172-
<div class="progress-fill" :style="'width: ' + (uploadLocalProgress * 100) + '%'"></div>
173-
</div>
174-
</div>
175167
</section>
176168

177169
<!-- Jobs Panel -->
@@ -214,6 +206,30 @@ <h2>Authentication Required</h2>
214206
</div>
215207
</div>
216208

209+
<!-- Folder Upload Error Modal -->
210+
<div class="modal" x-show="showFolderError" @click.self="showFolderError = false">
211+
<div class="modal-content">
212+
<div class="modal-icon">
213+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
214+
<path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
215+
<line x1="12" y1="11" x2="12" y2="15"/>
216+
<circle cx="12" cy="17" r="0.5" fill="currentColor"/>
217+
</svg>
218+
</div>
219+
<h2>Folder Upload Not Supported</h2>
220+
<p class="modal-description">
221+
Direct folder upload is not supported.<br>
222+
Please <strong>compress your folder into a ZIP file</strong> and upload the ZIP instead.
223+
</p>
224+
<div class="modal-hint">
225+
<code>zip -r my-folder.zip my-folder/</code>
226+
</div>
227+
<div class="form-actions">
228+
<button type="button" @click="showFolderError = false" class="btn btn-primary">Got it</button>
229+
</div>
230+
</div>
231+
</div>
232+
217233
<!-- Toast Notifications -->
218234
<div class="toast-container">
219235
<template x-for="toast in toasts" :key="toast.id">

frontend/static/app.js

Lines changed: 26 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Ragify Frontend Application
3-
* Alpine.js + HTMX based SPA
3+
* Alpine.js based SPA
44
*/
55

66
function ragifyApp() {
@@ -25,8 +25,7 @@ function ragifyApp() {
2525
uploadQueue: [],
2626
uploading: false,
2727
dragOver: false,
28-
uploadPhase: '', // 'zipping', 'uploading', ''
29-
uploadLocalProgress: 0,
28+
showFolderError: false,
3029

3130
// Search
3231
searchCollection: '',
@@ -68,15 +67,12 @@ function ragifyApp() {
6867
try {
6968
const res = await fetch('/api/collections', { credentials: 'include' });
7069
const data = await res.json();
71-
console.log('API response:', data);
7270
this.collections = data.collections || [];
73-
console.log('Collections loaded:', this.collections);
7471

7572
// Update stats
7673
this.stats.collections = this.collections.length;
7774
this.stats.chunks = this.collections.reduce((sum, c) => sum + (c.points_count || 0), 0);
7875
this.stats.documents = this.collections.reduce((sum, c) => sum + (c.documents_count || 0), 0);
79-
console.log('Stats updated:', this.stats);
8076

8177
// Set default upload/search collection if not set or invalid
8278
if (this.collections.length > 0) {
@@ -87,7 +83,6 @@ function ragifyApp() {
8783
if (!this.searchCollection || !collectionNames.includes(this.searchCollection)) {
8884
this.searchCollection = this.collections[0].name;
8985
}
90-
console.log('Default collections set:', this.uploadCollection, this.searchCollection);
9186
}
9287
} catch (e) {
9388
console.error('Failed to load collections:', e);
@@ -158,22 +153,31 @@ function ragifyApp() {
158153
}
159154
},
160155

161-
// Upload
156+
// Upload - simplified: files only, no folder handling
162157
handleDrop(event) {
163158
this.dragOver = false;
164-
const files = event.dataTransfer.files;
165-
this.addFilesToQueue(files);
159+
160+
// Check if any dropped item is a folder
161+
const items = event.dataTransfer.items;
162+
for (const item of items) {
163+
const entry = item.webkitGetAsEntry && item.webkitGetAsEntry();
164+
if (entry && entry.isDirectory) {
165+
this.showFolderError = true;
166+
return; // Block upload
167+
}
168+
}
169+
170+
this.addFilesToQueue(event.dataTransfer.files);
166171
},
167172

168173
handleFileSelect(event) {
169-
const files = event.target.files;
170-
this.addFilesToQueue(files);
174+
this.addFilesToQueue(event.target.files);
171175
event.target.value = ''; // Reset input
172176
},
173177

174178
addFilesToQueue(files) {
175179
for (const file of files) {
176-
// Avoid duplicates
180+
// Avoid duplicates by name
177181
if (!this.uploadQueue.find(f => f.name === file.name)) {
178182
this.uploadQueue.push(file);
179183
}
@@ -184,86 +188,38 @@ function ragifyApp() {
184188
if (this.uploadQueue.length === 0 || this.uploading) return;
185189
this.uploading = true;
186190

187-
// Decide: ZIP if multiple files OR total size > 5MB
188-
const totalSize = this.uploadQueue.reduce((sum, f) => sum + f.size, 0);
189-
const shouldZip = this.uploadQueue.length > 1 || totalSize > 5 * 1024 * 1024;
190-
191191
try {
192-
if (shouldZip) {
193-
await this.uploadAsZip();
194-
} else {
195-
await this.uploadSingleFile();
192+
// Upload each file - ZIP files go to /api/upload-zip, others to /api/upload
193+
for (const file of this.uploadQueue) {
194+
await this.uploadFile(file);
196195
}
197196
} catch (e) {
198197
this.showToast(`Upload failed: ${e.message || 'Unknown error'}`, 'error');
199198
}
200199

201200
this.uploadQueue = [];
202201
this.uploading = false;
203-
this.uploadPhase = '';
204-
this.uploadLocalProgress = 0;
205202
await this.loadJobs();
206203
},
207204

208-
async uploadSingleFile() {
209-
const file = this.uploadQueue[0];
210-
this.uploadPhase = 'uploading';
211-
this.uploadLocalProgress = 0;
205+
async uploadFile(file) {
206+
const isZip = file.name.toLowerCase().endsWith('.zip');
207+
const endpoint = isZip ? '/api/upload-zip' : '/api/upload';
212208

213209
const formData = new FormData();
214210
formData.append('file', file);
215211
formData.append('collection', this.uploadCollection);
216212

217-
const res = await fetch('/api/upload', {
218-
method: 'POST',
219-
body: formData,
220-
credentials: 'include'
221-
});
222-
223-
if (res.ok) {
224-
const data = await res.json();
225-
this.showToast(`Uploaded: ${file.name} (Job: ${data.job_id?.slice(0,8) || 'queued'})`, 'success');
226-
} else {
227-
const error = await res.json().catch(() => ({}));
228-
throw new Error(error.detail || res.statusText);
229-
}
230-
},
231-
232-
async uploadAsZip() {
233-
// Phase 1: Zipping
234-
this.uploadPhase = 'zipping';
235-
this.uploadLocalProgress = 0;
236-
237-
const zip = new JSZip();
238-
for (const file of this.uploadQueue) {
239-
zip.file(file.name, file);
240-
}
241-
242-
const blob = await zip.generateAsync({
243-
type: 'blob',
244-
compression: 'DEFLATE',
245-
compressionOptions: { level: 6 }
246-
}, (meta) => {
247-
this.uploadLocalProgress = meta.percent / 100;
248-
});
249-
250-
// Phase 2: Uploading
251-
this.uploadPhase = 'uploading';
252-
this.uploadLocalProgress = 0;
253-
254-
const formData = new FormData();
255-
formData.append('file', blob, 'upload.zip');
256-
formData.append('collection', this.uploadCollection);
257-
258-
const res = await fetch('/api/upload-zip', {
213+
const res = await fetch(endpoint, {
259214
method: 'POST',
260215
body: formData,
261216
credentials: 'include'
262217
});
263218

264219
if (res.ok) {
265220
const data = await res.json();
266-
this.showToast(`Uploaded ${this.uploadQueue.length} files as ZIP (Job: ${data.job_id?.slice(0,8) || 'queued'})`, 'success');
221+
const jobInfo = data.job_id ? ` (Job: ${data.job_id.slice(0,8)})` : '';
222+
this.showToast(`Uploaded: ${file.name}${jobInfo}`, 'success');
267223
} else {
268224
const error = await res.json().catch(() => ({}));
269225
throw new Error(error.detail || res.statusText);

frontend/static/style.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,16 @@ body {
313313
background: linear-gradient(180deg, #ffc970 0%, var(--amber) 100%);
314314
}
315315

316+
.btn-secondary {
317+
background: linear-gradient(180deg, var(--text) 0%, #555 100%);
318+
color: var(--panel-bg);
319+
border-color: #555;
320+
}
321+
322+
.btn-secondary:hover {
323+
background: linear-gradient(180deg, #888 0%, var(--text) 100%);
324+
}
325+
316326
.btn-danger {
317327
background: linear-gradient(180deg, var(--red) 0%, #8b0000 100%);
318328
border-color: #8b0000;
@@ -715,6 +725,27 @@ body {
715725
line-height: 1.5;
716726
}
717727

728+
.modal-icon {
729+
text-align: center;
730+
margin-bottom: 1rem;
731+
color: var(--amber);
732+
}
733+
734+
.modal-hint {
735+
background: var(--gauge-bg);
736+
border: 1px solid var(--metal);
737+
border-radius: 4px;
738+
padding: 0.75rem 1rem;
739+
text-align: center;
740+
margin-bottom: 0.5rem;
741+
}
742+
743+
.modal-hint code {
744+
color: var(--green);
745+
font-family: 'Share Tech Mono', monospace;
746+
font-size: 0.9rem;
747+
}
748+
718749
.form-actions {
719750
display: flex;
720751
justify-content: flex-end;

0 commit comments

Comments
 (0)