2026-06-05 17:12:44 +00:00
import { Router , Request , Response } from "express" ;
2026-06-05 17:05:27 +00:00
import {
requests ,
donors ,
whatsappLog ,
checkEligibility ,
STATUS_STEP ,
DonationRequest ,
WhatsappLogEntry ,
} from "../lib/mockDb.js" ;
import { v4 as uuidv4 } from "uuid" ;
const router = Router ( ) ;
2026-06-05 17:12:44 +00:00
// ─── GET /requests ────────────────────────────────────────────────────────────
router . get ( "/requests" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
let result = [ . . . requests ] ;
const { status , needType } = req . query ;
if ( status ) result = result . filter ( ( r ) = > r . status === status ) ;
if ( needType ) result = result . filter ( ( r ) = > r . needType === needType ) ;
res . json ( result ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── GET /requests/new ───────────────────────────────────────────────────────
router . get ( "/requests/new" , ( _req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
res . json ( requests . filter ( ( r ) = > r . status === "new" ) ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── GET /requests/published ─────────────────────────────────────────────────
router . get ( "/requests/published" , ( _req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
res . json ( requests . filter ( ( r ) = > r . status === "published" ) ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests ───────────────────────────────────────────────────────────
router . post ( "/requests" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
const {
beneficiaryName ,
nationalId ,
phone ,
source ,
sourceName ,
needType ,
requestedAmount ,
description ,
} = req . body ;
if (
! beneficiaryName ||
! nationalId ||
! phone ||
! source ||
! sourceName ||
! needType ||
! requestedAmount ||
! description
) {
2026-06-05 17:12:44 +00:00
res . status ( 400 ) . json ( { error : "Missing required fields" } ) ;
return ;
2026-06-05 17:05:27 +00:00
}
const { eligible } = checkEligibility ( nationalId ) ;
let status : DonationRequest [ "status" ] ;
if ( eligible === true ) {
status = "verified" ;
} else if ( eligible === false ) {
status = "rejected" ;
} else {
status = "pending_review" ;
}
const now = new Date ( ) . toISOString ( ) ;
const caseNum = String ( requests . length + 1 ) . padStart ( 3 , "0" ) ;
const newReq : DonationRequest = {
id : uuidv4 ( ) ,
caseId : ` CASE- ${ caseNum } ` ,
beneficiaryName ,
nationalId ,
phone ,
source ,
sourceName ,
needType ,
requestedAmount : Number ( requestedAmount ) ,
collectedAmount : 0 ,
description ,
status ,
currentStep : STATUS_STEP [ status ] ,
donorId : null ,
donorName : null ,
thankYouMessage : null ,
whatsappStatus : null ,
whatsappSentAt : null ,
rejectionReason :
status === "rejected"
? "المستفيد غير مؤهل وفق قاعدة بيانات الاستحقاق"
: null ,
createdAt : now ,
updatedAt : now ,
} ;
requests . push ( newReq ) ;
2026-06-05 17:12:44 +00:00
res . status ( 201 ) . json ( newReq ) ;
2026-06-05 17:05:27 +00:00
} ) ;
2026-06-05 17:12:44 +00:00
// ─── GET /requests/:id ────────────────────────────────────────────────────────
router . get ( "/requests/:id" , ( req : Request , res : Response ) : void = > {
const item = requests . find (
( r ) = > r . id === req . params . id || r . caseId === req . params . id
) ;
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
}
2026-06-05 17:05:27 +00:00
res . json ( item ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── Helper: find & update ────────────────────────────────────────────────────
2026-06-05 17:05:27 +00:00
function findAndUpdate (
id : string ,
updater : ( r : DonationRequest ) = > void ,
2026-06-05 17:12:44 +00:00
res : Response
) : void {
2026-06-05 17:05:27 +00:00
const item = requests . find ( ( r ) = > r . id === id || r . caseId === id ) ;
2026-06-05 17:12:44 +00:00
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
}
2026-06-05 17:05:27 +00:00
updater ( item ) ;
item . updatedAt = new Date ( ) . toISOString ( ) ;
res . json ( item ) ;
}
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/verify ────────────────────────────────────────────────
router . post ( "/requests/:id/verify" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
findAndUpdate (
req . params . id ,
( r ) = > {
r . status = "verified" ;
r . currentStep = STATUS_STEP [ "verified" ] ;
} ,
res
) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/publish ───────────────────────────────────────────────
router . post ( "/requests/:id/publish" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
findAndUpdate (
req . params . id ,
( r ) = > {
r . status = "published" ;
r . currentStep = STATUS_STEP [ "published" ] ;
} ,
res
) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/donate ────────────────────────────────────────────────
router . post ( "/requests/:id/donate" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
const item = requests . find (
( r ) = > r . id === req . params . id || r . caseId === req . params . id
) ;
2026-06-05 17:12:44 +00:00
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
}
2026-06-05 17:05:27 +00:00
const { donorName , donorPhone , donorEmail , amount } = req . body ;
if ( ! donorName || ! donorPhone || ! amount ) {
2026-06-05 17:12:44 +00:00
res . status ( 400 ) . json ( { error : "Missing donor details" } ) ;
return ;
2026-06-05 17:05:27 +00:00
}
const donorId = uuidv4 ( ) ;
const existingDonor = donors . find ( ( d ) = > d . phone === donorPhone ) ;
if ( existingDonor ) {
existingDonor . totalDonated += Number ( amount ) ;
existingDonor . donationCount += 1 ;
item . donorId = existingDonor . id ;
} else {
const newDonor = {
id : donorId ,
name : donorName ,
phone : donorPhone ,
email : donorEmail || null ,
totalDonated : Number ( amount ) ,
donationCount : 1 ,
} ;
donors . push ( newDonor ) ;
item . donorId = donorId ;
}
item . donorName = donorName ;
item . collectedAmount = Number ( amount ) ;
item . status = "donated" ;
item . currentStep = STATUS_STEP [ "donated" ] ;
item . updatedAt = new Date ( ) . toISOString ( ) ;
res . json ( item ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/deliver ───────────────────────────────────────────────
router . post ( "/requests/:id/deliver" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
findAndUpdate (
req . params . id ,
( r ) = > {
r . status = "delivered" ;
r . currentStep = STATUS_STEP [ "delivered" ] ;
} ,
res
) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/confirm-receipt ───────────────────────────────────────
router . post (
"/requests/:id/confirm-receipt" ,
( req : Request , res : Response ) : void = > {
findAndUpdate (
req . params . id ,
( r ) = > {
r . status = "receipt_confirmed" ;
r . currentStep = STATUS_STEP [ "receipt_confirmed" ] ;
} ,
res
) ;
}
) ;
2026-06-05 17:05:27 +00:00
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/thank-you ─────────────────────────────────────────────
router . post ( "/requests/:id/thank-you" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
const item = requests . find (
( r ) = > r . id === req . params . id || r . caseId === req . params . id
) ;
2026-06-05 17:12:44 +00:00
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
}
const { message , beneficiaryName } = req . body ;
if ( ! message ) {
res . status ( 400 ) . json ( { error : "Message is required" } ) ;
return ;
}
2026-06-05 17:05:27 +00:00
2026-06-05 17:12:44 +00:00
// Update beneficiary name if provided
if ( beneficiaryName ) {
item . beneficiaryName = beneficiaryName ;
}
2026-06-05 17:05:27 +00:00
item . thankYouMessage = message ;
item . status = "thank_you_submitted" ;
item . currentStep = STATUS_STEP [ "thank_you_submitted" ] ;
item . whatsappStatus = "pending" ;
item . updatedAt = new Date ( ) . toISOString ( ) ;
// Create whatsapp log entry
const logEntry : WhatsappLogEntry = {
id : uuidv4 ( ) ,
caseId : item.caseId ,
donorName : item.donorName || "متبرع" ,
donorPhone : donors.find ( ( d ) = > d . id === item . donorId ) ? . phone || "" ,
beneficiaryMessage : message ,
whatsappMessage : ` السلام عليكم، نشكركم على تبرعكم عبر منصة إحسان. \ nتم إيصال الدعم للمستفيد، وهذه رسالة الشكر من المستفيد: \ n" ${ message } " \ nرقم الحالة: ${ item . caseId } ` ,
status : "pending" ,
sentAt : null ,
createdAt : new Date ( ) . toISOString ( ) ,
} ;
whatsappLog . push ( logEntry ) ;
res . json ( item ) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/send-whatsapp ─────────────────────────────────────────
router . post (
"/requests/:id/send-whatsapp" ,
async ( req : Request , res : Response ) : Promise < void > = > {
const item = requests . find (
( r ) = > r . id === req . params . id || r . caseId === req . params . id
) ;
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
2026-06-05 17:05:27 +00:00
}
2026-06-05 17:12:44 +00:00
const simulate = process . env . OPENCLAW_SIMULATE !== "false" ;
const openclawUrl = process . env . OPENCLAW_BASE_URL || "http://localhost:3100" ;
const logEntry = whatsappLog . find ( ( w ) = > w . caseId === item . caseId ) ;
const now = new Date ( ) . toISOString ( ) ;
if ( simulate ) {
item . whatsappStatus = "sent" ;
item . whatsappSentAt = now ;
item . status = "whatsapp_sent" ;
item . currentStep = STATUS_STEP [ "whatsapp_sent" ] ;
item . updatedAt = now ;
if ( logEntry ) {
logEntry . status = "sent" ;
logEntry . sentAt = now ;
}
res . json ( {
success : true ,
message : "WhatsApp sent (simulated)" ,
simulated : true ,
sentAt : now ,
} ) ;
return ;
2026-06-05 17:05:27 +00:00
}
2026-06-05 17:12:44 +00:00
// Live mode — call OpenClaw
try {
const donor = donors . find ( ( d ) = > d . id === item . donorId ) ;
const payload = {
to : donor?.phone || "" ,
message : logEntry?.whatsappMessage || "" ,
caseId : item.caseId ,
} ;
const response = await fetch ( ` ${ openclawUrl } /api/whatsapp/send ` , {
method : "POST" ,
headers : { "Content-Type" : "application/json" } ,
body : JSON.stringify ( payload ) ,
} ) ;
if ( ! response . ok ) {
throw new Error ( ` OpenClaw returned ${ response . status } ` ) ;
}
item . whatsappStatus = "sent" ;
item . whatsappSentAt = now ;
item . status = "whatsapp_sent" ;
item . currentStep = STATUS_STEP [ "whatsapp_sent" ] ;
item . updatedAt = now ;
if ( logEntry ) {
logEntry . status = "sent" ;
logEntry . sentAt = now ;
}
res . json ( {
success : true ,
message : "WhatsApp sent via OpenClaw" ,
simulated : false ,
sentAt : now ,
} ) ;
} catch ( err : unknown ) {
const message = err instanceof Error ? err . message : "Unknown error" ;
item . whatsappStatus = "failed" ;
item . updatedAt = now ;
if ( logEntry ) {
logEntry . status = "failed" ;
}
res . json ( {
success : false ,
message : ` Failed to send via OpenClaw: ${ message } ` ,
simulated : false ,
sentAt : null ,
} ) ;
2026-06-05 17:05:27 +00:00
}
}
2026-06-05 17:12:44 +00:00
) ;
2026-06-05 17:05:27 +00:00
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/close ─────────────────────────────────────────────────
router . post ( "/requests/:id/close" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
findAndUpdate (
req . params . id ,
( r ) = > {
r . status = "closed" ;
r . currentStep = STATUS_STEP [ "closed" ] ;
} ,
res
) ;
} ) ;
2026-06-05 17:12:44 +00:00
// ─── POST /requests/:id/reject ────────────────────────────────────────────────
router . post ( "/requests/:id/reject" , ( req : Request , res : Response ) : void = > {
2026-06-05 17:05:27 +00:00
const item = requests . find (
( r ) = > r . id === req . params . id || r . caseId === req . params . id
) ;
2026-06-05 17:12:44 +00:00
if ( ! item ) {
res . status ( 404 ) . json ( { error : "Not found" } ) ;
return ;
}
2026-06-05 17:05:27 +00:00
item . status = "rejected" ;
item . currentStep = STATUS_STEP [ "rejected" ] ;
item . rejectionReason = req . body ? . reason || "تم الرفض من قبل المشرف" ;
item . updatedAt = new Date ( ) . toISOString ( ) ;
res . json ( item ) ;
} ) ;
export default router ;