feat: extract file text on every attach path and backfill on reindex

- inline and signed uploads queue the text job in their transaction
- the admin form queues it in the save transaction
- detach resyncs the material (its text row goes by cascade)
- bm:materials-reindex backfills missing PDF texts before each sync
This commit is contained in:
Jakub Zych
2026-11-08 20:16:14 +01:00
parent 412821ec56
commit f841e8ed71
11 changed files with 480 additions and 22 deletions

View File

@@ -208,7 +208,8 @@ func sizeMB(n int64) string {
}
// DetachMaterialFile answers DELETE /materials/{id}/files/{file}: the file
// row goes, then its blob and thumbnails. A file of another material is a
// row goes (its extracted text with it), the material is resynced, then the
// blob and thumbnails go. A file of another material is a
// 404.
func (h *Handlers) DetachMaterialFile(w http.ResponseWriter, r *http.Request) {
db, ok := h.db(w, r)
@@ -242,6 +243,9 @@ func (h *Handlers) DetachMaterialFile(w http.ResponseWriter, r *http.Request) {
writeOpaque500(w)
return
}
// The file's text row went with it (foreign-key cascade); the resync
// drops the text from the material's document.
h.MaterialIndex.Sync(r.Context(), db, m.ID)
// The row is gone, so the file is no longer served; a blob left behind
// by a failed delete is only disk space.
_ = attach.DeleteKeys(r.Context(), bucket, attach.BlobKeys(f))

View File

@@ -134,6 +134,10 @@ func (h *Handlers) AttachMaterialInline(w http.ResponseWriter, r *http.Request)
err := lagoon.Transaction(r.Context(), db, func(ctx context.Context, tx *gorm.DB) error {
f, why, err := storeMaterialFile(ctx, tx, bucket, m, name, bytes.NewReader(data), lim)
stored, refusal = f, why
if err == nil && why == "" && f != nil {
// The text job joins the transaction and syncs the material.
h.MaterialIndex.QueueExtract(ctx, tx, m.ID)
}
return err
})
if err != nil || refusal != "" {
@@ -302,6 +306,8 @@ func (h *Handlers) SignedMaterialUpload(w http.ResponseWriter, r *http.Request)
refusal = singleFilePart
return errUploadRefused
}
// The text job joins the transaction and syncs the material.
h.MaterialIndex.QueueExtract(ctx, tx, m.ID)
return nil
})
if err != nil {