@@ -18,12 +18,52 @@ export async function startServer({ port = 3030, dbPath = DEFAULT_DB_PATH } = {}
1818
1919 // API Routes
2020
21- let currentScanProgress = { current : 0 , total : 0 , file : "" } ;
21+ let currentScanProgress = { current : 0 , total : 0 , file : "" , status : "idle" } ;
22+ let scanAbortController = null ;
2223
2324 app . get ( "/api/scan/progress" , ( req , res ) => {
2425 res . json ( currentScanProgress ) ;
2526 } ) ;
2627
28+ app . post ( "/api/scan/start" , async ( req , res ) => {
29+ const { directory, skippedFiles } = req . body ;
30+ if ( ! directory ) return res . status ( 400 ) . json ( { error : "Directory path required" } ) ;
31+
32+ // Prevent multiple concurrent scans for now
33+ if ( currentScanProgress . status === "running" ) {
34+ return res . status ( 409 ) . json ( { error : "A scan is already in progress" } ) ;
35+ }
36+
37+ currentScanProgress = { current : 0 , total : 0 , file : "" , status : "running" } ;
38+
39+ // Fire and forget the scan job
40+ ( async ( ) => {
41+ try {
42+ const audit = await buildLocalAudit ( {
43+ dbPath,
44+ targetDirectory : directory ,
45+ skippedFiles : skippedFiles || [ ] ,
46+ onProgress : ( current , total , file ) => {
47+ currentScanProgress = { current, total, file, status : "running" } ;
48+ }
49+ } ) ;
50+
51+ if ( audit . needsPassword ) {
52+ currentScanProgress . status = "needs_password" ;
53+ currentScanProgress . passwordFile = audit . passwordFile ;
54+ } else {
55+ currentScanProgress . status = "complete" ;
56+ }
57+ } catch ( error ) {
58+ console . error ( "Background Scan Error:" , error ) ;
59+ currentScanProgress . status = "failed" ;
60+ currentScanProgress . error = error . message ;
61+ }
62+ } ) ( ) ;
63+
64+ res . json ( { success : true , message : "Scan started in background" } ) ;
65+ } ) ;
66+
2767 app . get ( "/api/select-directory" , async ( req , res ) => {
2868 try {
2969 const { execSync } = await import ( "node:child_process" ) ;
@@ -42,74 +82,6 @@ export async function startServer({ port = 3030, dbPath = DEFAULT_DB_PATH } = {}
4282 }
4383 } ) ;
4484
45- app . post ( "/api/add-password" , async ( req , res ) => {
46- try {
47- const { password, filePath } = req . body ;
48-
49- // Test the password immediately if filePath is provided
50- if ( filePath ) {
51- const fs = await import ( "node:fs/promises" ) ;
52- const pdf = ( await import ( "pdf-parse" ) ) . default ;
53- const dataBuffer = await fs . readFile ( filePath ) ;
54-
55- let isValid = false ;
56-
57- const originalWarn = console . warn ;
58- console . warn = ( ) => { } ;
59- try {
60- await pdf ( dataBuffer , { password } ) ;
61- isValid = true ;
62- } catch ( e ) {
63- isValid = false ;
64- } finally {
65- console . warn = originalWarn ;
66- }
67-
68- if ( ! isValid ) {
69- return res . json ( { success : false , error : "Incorrect password for this file" } ) ;
70- }
71- }
72-
73- const { loadConfig, saveConfig } = await import ( "./core/config.js" ) ;
74- const { config, configPath } = await loadConfig ( ) ;
75- if ( ! config . pdfPasswords ) config . pdfPasswords = [ ] ;
76- if ( ! config . pdfPasswords . includes ( password ) ) {
77- config . pdfPasswords . push ( password ) ;
78- await saveConfig ( config , configPath ) ;
79- }
80- res . json ( { success : true } ) ;
81- } catch ( e ) {
82- res . status ( 500 ) . json ( { error : e . message } ) ;
83- }
84- } ) ;
85-
86- // Step 1 & 2: Start Scan
87- app . post ( "/api/scan/start" , async ( req , res ) => {
88- try {
89- const { directory, skippedFiles } = req . body ;
90- if ( ! directory ) return res . status ( 400 ) . json ( { error : "Directory path required" } ) ;
91-
92- currentScanProgress = { current : 0 , total : 0 , file : "" } ;
93- const audit = await buildLocalAudit ( {
94- dbPath,
95- targetDirectory : directory ,
96- skippedFiles : skippedFiles || [ ] ,
97- onProgress : ( current , total , file ) => {
98- currentScanProgress = { current, total, file } ;
99- }
100- } ) ;
101-
102- if ( audit . needsPassword ) {
103- res . json ( { success : true , message : "Password required" , needsPassword : true , passwordFile : audit . passwordFile } ) ;
104- return ;
105- }
106-
107- res . json ( { success : true , message : "Scan complete" , needsPassword : false } ) ;
108- } catch ( error ) {
109- res . status ( 500 ) . json ( { error : error . message } ) ;
110- }
111- } ) ;
112-
11385 function cleanJSON ( str ) {
11486 try {
11587 const match = str . match ( / ` ` ` (?: j s o n ) ? \s * ( [ \s \S ] * ?) \s * ` ` ` / ) ;
@@ -198,6 +170,17 @@ Return ONLY a raw JSON string like {"proposedName": "file.pdf", "reasoning": "wh
198170 }
199171 } ) ;
200172
173+ app . post ( "/api/items/:id/reject" , async ( req , res ) => {
174+ try {
175+ const { id } = req . params ;
176+ catalog . db . prepare ( "UPDATE review_items SET status = 'rejected', approved = 0, updated_at = ? WHERE id = ?" )
177+ . run ( new Date ( ) . toISOString ( ) , id ) ;
178+ res . json ( { success : true } ) ;
179+ } catch ( error ) {
180+ res . status ( 500 ) . json ( { error : error . message } ) ;
181+ }
182+ } ) ;
183+
201184 app . post ( "/api/apply" , async ( req , res ) => {
202185 try {
203186 const result = await applyApprovedReview ( { catalog } ) ;
0 commit comments