Persist offer assistant jobs and recover results across closed views

This commit is contained in:
2026-09-06 12:35:30 +03:00
parent 2e16a68565
commit f830625b60
12 changed files with 297 additions and 3 deletions
+37
View File
@@ -1,3 +1,5 @@
import * as aiJobs from './ai-job-store.mjs';
import { startAiJobRunner } from './ai-job-runner.mjs';
import { observeTenders, tenderHistory } from './tender-history.mjs';
import { requestLifetime } from './request-lifetime.mjs';
import {documentReview,saveDocumentReview,reviewedAnalysisSource} from './document-review.mjs';
@@ -684,9 +686,42 @@ async function tendersPayload(force = false) {
};
}
const aiJobRunner = startAiJobRunner(async (job,{signal}) => {
if(!aiConfigured())throw new Error('AI hizmeti kullanılamıyor. Ayarlar düzeltildikten sonra yeniden başlatın.');
const body=job.input;
const shared={signal,ikn:body.ikn,tender:body.tender,chain:body.chain,text:body.text};
const result=job.operation==='belge-sor' ? await belgeyeSor({...shared,soru:body.soru})
: job.operation==='teklif-denetim' ? await teklifDenetle({...shared,taslak:body.taslak,gereklilikler:body.gereklilikler})
: await teklifTaslak({...shared,tur:body.tur,firma:body.firma,belgeler:body.belgeler,fiyat:body.fiyat,gereklilikler:body.gereklilikler});
return {success:true,...result};
});
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://localhost:${PORT}`);
try {
if (url.pathname === '/api/me/ai-jobs') {
const user=userFromAuthHeader(req.headers.authorization);
if(!user)return json(res,401,{success:false,error:'Oturum gerekli.'});
const id=url.searchParams.get('id');
if(req.method==='GET') {
if(id){const job=aiJobs.getAiJob(user.email,id);return json(res,job?200:404,{success:!!job,job,error:job?undefined:'İş bulunamadı.'});}
return json(res,200,{success:true,jobs:aiJobs.listAiJobs(user.email,url.searchParams.get('ikn')).map(({result,...job})=>job)});
}
if(req.method==='DELETE') {
const ok=aiJobs.cancelAiJob(user.email,id);if(ok)aiJobRunner.cancel(id);
return json(res,ok?200:404,{success:ok,error:ok?undefined:'İş bulunamadı veya artık iptal edilemiyor.'});
}
if(req.method==='POST') {
try {
if(!aiConfigured())return json(res,503,{success:false,error:'AI hizmeti şu anda kullanılamıyor. Daha sonra yeniden deneyin.'});
const body=await readBody(req,2*1024*1024);
if(id) {if(body.action!=='retry'||!aiJobs.retryAiJob(user.email,id))return json(res,404,{success:false,error:'Yeniden başlatılabilir iş bulunamadı.'});}
const job=id?aiJobs.getAiJob(user.email,id):aiJobs.createAiJob(user.email,body.requestId,body.operation,body.input);
aiJobRunner.wake();return json(res,202,{success:true,job});
}catch(error){return json(res,400,{success:false,error:error.message});}
}
return json(res,405,{success:false,error:'Desteklenmeyen metot.'});
}
if (url.pathname === '/api/exchange-rates') {
try { return json(res,200,{success:true,...await exchangeRates()}); }
catch(e) { return json(res,503,{success:false,error:e.message}); }
@@ -1605,3 +1640,5 @@ arkaPlandaIsit();
// Bildirim işi tender verisine ihtiyaç duyar; enjekte ederek başlat
startScheduler({ tenderGetir: () => tendersPayload() });
});
server.on('close',()=>aiJobRunner.stop());