feat: import review step + admin notifications
Some checks failed
Release / Test backend (push) Failing after 16s
Release / Check ui (push) Failing after 33s
Release / Docker (push) Has been skipped
Release / Gitea Release (push) Has been skipped

- Import page: add file upload with review step before committing
- Backend: add analyze endpoint to preview chapters before import
- Add notifications collection + API for admin alerts
- Add bell icon in header with notification dropdown (admin only)
- Runner creates notification on import completion
- Notifications link to /admin/import for easy review
This commit is contained in:
root
2026-04-09 10:30:36 +05:00
parent bb55afb562
commit ab92bf84bb
7 changed files with 297 additions and 34 deletions

View File

@@ -20,8 +20,14 @@ type importRequest struct {
}
type importResponse struct {
TaskID string `json:"task_id"`
Slug string `json:"slug"`
TaskID string `json:"task_id"`
Slug string `json:"slug"`
Preview *importPreview `json:"preview,omitempty"`
}
type importPreview struct {
Chapters int `json:"chapters"`
FirstLines []string `json:"first_lines"`
}
func (s *Server) handleAdminImport(w http.ResponseWriter, r *http.Request) {
@@ -42,6 +48,7 @@ func (s *Server) handleAdminImport(w http.ResponseWriter, r *http.Request) {
req.Title = r.FormValue("title")
req.FileName = r.FormValue("file_name")
req.FileType = r.FormValue("file_type")
analyzeOnly := r.FormValue("analyze") == "true"
file, header, err := r.FormFile("file")
if err != nil {
@@ -63,7 +70,16 @@ func (s *Server) handleAdminImport(w http.ResponseWriter, r *http.Request) {
return
}
// Upload to MinIO directly via the store
// Analyze only - just count chapters
if analyzeOnly {
preview := analyzeImportFile(data, req.FileType)
writeJSON(w, 0, importResponse{
Preview: preview,
})
return
}
// Upload to MinIO for actual import
objectKey = fmt.Sprintf("imports/%d_%s", time.Now().Unix(), header.Filename)
store, ok := s.deps.Producer.(*storage.Store)
if !ok {
@@ -111,6 +127,27 @@ func (s *Server) handleAdminImport(w http.ResponseWriter, r *http.Request) {
})
}
// analyzeImportFile does a quick scan of the file to count chapters.
// This is a placeholder - real implementation would parse PDF/EPUB properly.
func analyzeImportFile(data []byte, fileType string) *importPreview {
// TODO: Implement actual PDF/EPUB parsing to count chapters
// For now, estimate based on file size
preview := &importPreview{
Chapters: estimateChapters(data, fileType),
FirstLines: []string{},
}
return preview
}
func estimateChapters(data []byte, fileType string) int {
// Rough estimate: ~100KB per chapter for PDF, ~50KB for EPUB
size := len(data)
if fileType == "pdf" {
return size / 100000
}
return size / 50000
}
func (s *Server) handleAdminImportStatus(w http.ResponseWriter, r *http.Request) {
taskID := r.PathValue("id")
if taskID == "" {

View File

@@ -249,6 +249,10 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
mux.HandleFunc("GET /api/admin/import", s.handleAdminImportList)
mux.HandleFunc("GET /api/admin/import/{id}", s.handleAdminImportStatus)
// Notifications
mux.HandleFunc("GET /api/notifications", s.handleListNotifications)
mux.HandleFunc("PATCH /api/notifications/{id}", s.handleMarkNotificationRead)
// Voices list
mux.HandleFunc("GET /api/voices", s.handleVoices)