The printing system has been redesigned to use MongoDB as the single source of truth, eliminating API communication between the website and printing server. Both systems read from and write to the same MongoDB database.
┌─────────────────┐ ┌─────────────────┐
│ Website │ │ Printing Server │
│ (Next.js) │ │ (Node.js) │
└────────┬────────┘ └────────┬────────┘
│ │
│ │
└──────────┬─────────────────┘
│
▼
┌──────────────────────┐
│ MongoDB Database │
│ (Single Source of │
│ Truth) │
└──────────────────────┘
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ orders │ │printers│ │print_ │
│ │ │ │ │ logs │
└────────┘ └────────┘ └────────┘
flowchart TD
A[Order Created] -->|paymentStatus: completed| B[Set printStatus: pending]
B --> C[Printing Server Polls MongoDB]
C -->|findOneAndUpdate atomic| D[Set printStatus: printing]
D --> E[Download File from Cloudinary]
E --> F[Send to Printer]
F -->|Success| G[Set printStatus: printed]
F -->|Failure| H[Set printStatus: pending + error]
H --> C
pending → printing → printed
↑ │
└─────────┘ (on failure)
State Rules:
- Only
pendingorders can be claimed - State transitions are atomic (using
findOneAndUpdate) - Duplicate printing is impossible:
- Atomic claim prevents race conditions
printJobIdidempotency check prevents re-printing- Worker ownership ensures only one worker processes each job
{
_id: ObjectId,
orderId: string,
paymentStatus: 'pending' | 'completed' | 'failed',
printStatus: 'pending' | 'printing' | 'printed',
printError?: string,
printerId?: string,
printerName?: string,
printStartedAt?: Date,
printCompletedAt?: Date,
// Production-critical fields
printJobId?: string, // UUID for idempotency (unique, indexed)
printAttempt?: number, // Number of print attempts (default: 0)
printingBy?: string, // Worker ID that owns this job (indexed)
fileURL: string,
printingOptions: {...},
// ... other fields
}Indexes:
{ printStatus: 1, paymentStatus: 1, createdAt: 1 }{ printStatus: 1, createdAt: -1 }{ printerId: 1, printStatus: 1 }{ printJobId: 1 }(unique, sparse){ printingBy: 1, printStatus: 1 }
{
_id: ObjectId,
name: string,
printer_id: string, // NEW
printer_name: string, // NEW
status: 'online' | 'offline' | 'busy' | 'error',
last_seen_at: Date, // NEW
last_successful_print_at?: Date, // NEW
queue_length: number, // NEW
error_message?: string, // NEW
driver_name?: string, // NEW
system_name: 'Windows' | 'Linux', // NEW
connectionType: 'usb' | 'network' | 'wireless',
isActive: boolean,
autoPrintEnabled: boolean,
// ... other fields
}Indexes:
{ status: 1, isActive: 1 }{ printer_id: 1 }(unique){ last_seen_at: -1 }
{
_id: ObjectId,
action: string, // 'reprint', 'cancel', 'reset_state', 'force_printed', 'server_shutdown'
orderId: string,
printJobId?: string, // Print job ID for idempotency tracking
adminId?: string,
adminEmail?: string,
previousStatus?: string,
newStatus?: string,
reason?: string,
timestamp: Date,
metadata?: object
}Indexes:
{ orderId: 1, timestamp: -1 }{ timestamp: -1 }{ action: 1, timestamp: -1 }
{
_id: ObjectId,
timestamp: Date,
prints_per_hour: number,
failures_per_hour: number,
average_print_start_delay: number, // seconds
printer_offline_duration: number, // seconds
workerId: string,
createdAt: Date,
updatedAt: Date
}Indexes:
{ timestamp: -1 }{ workerId: 1, timestamp: -1 }
-
Poll MongoDB every 5 seconds (configurable) for orders with:
printStatus: 'pending'paymentStatus: 'completed'- Has file URL(s)
-
Validate Capabilities:
- Check order requirements against printer capabilities
- Skip orders that don't match (page size, color, duplex, copies)
-
Atomically Claim Order:
const printJobId = randomUUID(); const workerId = getWorkerId(); const order = await Order.findOneAndUpdate( { _id: orderId, printStatus: 'pending', paymentStatus: 'completed' }, { $set: { printStatus: 'printing', printStartedAt: new Date(), printJobId: printJobId, printingBy: workerId, printAttempt: 1, ... } }, { new: true } );
-
Check Idempotency:
- Verify
printJobIdhas not been printed before - Skip printing if already printed
- Verify
-
Process Order:
- Download file from Cloudinary
- Validate file (existence, size, PDF header)
- Check idempotency: skip if
printJobIdalready printed - Send to printer
- On success: Update to
printStatus: 'printed'(only if owned by this worker) - On failure: Reset to
printStatus: 'pending'with error message (only if owned by this worker) - Increment
printAttempton failure
-
Check Health every 30 seconds (configurable):
- Query Windows Print Spooler for printer status
- Update MongoDB
printerscollection with:- Status (online/offline/busy/error)
- Queue length
- Last seen timestamp
- Error messages
-
Only Process Orders if printer is online
-
Auto-Recovery: When printer comes back online, processing resumes automatically
- Orders in
printingstate for > 30 minutes are automatically reset topending - Logged to
print_logsfor admin review
The admin monitor page (/admin/printer-monitor) uses:
- MongoDB Change Streams (if replica set available)
- Polling Fallback (every 2-3 seconds) if replica set not available
-
Order Groups:
- Pending Orders
- Printing Orders
- Printed Orders (last 24 hours)
-
Printer Health Panel:
- Status indicator (color-coded)
- Queue length
- Last successful print
- Error messages
-
Admin Actions:
- Reprint failed order
- Cancel pending order
- Reset stuck order
- Force mark as printed
All actions are logged to print_logs collection.
The system uses multiple layers of protection:
- Atomic Claim with Ownership:
const printJobId = randomUUID();
const workerId = getWorkerId();
const order = await Order.findOneAndUpdate(
{
_id: orderId,
printStatus: 'pending', // Condition: only update if still pending
paymentStatus: 'completed'
},
{
$set: {
printStatus: 'printing',
printStartedAt: new Date(),
printJobId: printJobId, // Unique UUID
printingBy: workerId, // Worker ownership
printAttempt: 1,
printerId: printerId,
printerName: printerName
}
},
{ new: true }
);- Idempotency Check:
// Before printing, check if printJobId already printed
const alreadyPrinted = await Order.findOne({
printJobId: printJobId,
printStatus: 'printed'
});
if (alreadyPrinted) {
// Skip printing - already done
return;
}- Ownership Verification:
// Only allow owning worker to complete/reset
const result = await Order.findOneAndUpdate(
{
_id: orderId,
printingBy: workerId, // Must be owned by this worker
printStatus: 'printing'
},
{ $set: { printStatus: 'printed', ... } }
);This ensures:
- Only one printing server can claim an order (atomic claim)
- No duplicate printing (printJobId idempotency)
- Safe concurrent access (worker ownership)
- Graceful handling of server restarts (ownership-based recovery)
- Orders remain in
pendingstate - Printer status updated to
offlinein MongoDB - Admin UI shows printer as offline
- Processing resumes when printer comes back online
- Offline duration tracked in metrics
- Order reset to
pendingwith error message (only by owning worker) - Error logged in
printErrorfield printAttemptincrementedprintJobIdcleared to allow new attempt- Printing server will retry on next poll
- Admin can manually reprint if needed
- Failures tracked in metrics
- Auto-detected after 30 minutes (only orders owned by this worker)
- Automatically reset to
pending - Logged to
print_logswith reason - Other workers' orders are ignored
- On shutdown (SIGTERM, SIGINT):
- Find all orders with
printStatus: 'printing'ANDprintingBy: workerId - Reset them to
'pending'with reason "Server shutdown" - Log each reset to
print_logs - Ensures no jobs are left stuck
- Find all orders with
- Orders that don't match printer capabilities are skipped
- Marked as failed with capability mismatch error
- Admin can see capability requirements in error message
- Invalid files (missing, too small, invalid PDF) are rejected
- Order marked as failed with validation error
- Admin can see specific validation error
- All admin actions require authentication
- Actions are logged with:
- Admin email
- Timestamp
- Previous and new status
- Reason
- Website and printing server use same MongoDB connection
- No API keys or authentication between systems
- MongoDB handles access control
Critical indexes for performance:
orders.printStatus + paymentStatus + createdAtorders.printStatus + createdAtorders.printJobId(unique, sparse)orders.printingBy + printStatusprinters.status + isActiveprint_logs.orderId + timestampmetrics.timestamp(for metrics queries)metrics.workerId + timestamp
- Order polling: 5 seconds (configurable)
- Health checks: 30 seconds (configurable)
- Metrics storage: 5 minutes
- Stuck order check: 5 minutes
- Admin UI polling: 2-3 seconds (if Change Streams not available)
- Each print job has unique
printJobId(UUID) - System checks if
printJobIdalready printed before printing - Prevents duplicate physical prints
printAttemptcounter tracks retry attempts
- Each server has unique
workerId(UUID + hostname + timestamp) - Orders claimed with
printingBy: workerId - Only owning worker can complete/reset job
- Supports multiple servers safely
- Prevents interference between workers
- On shutdown, resets orders owned by this worker
- Logs all shutdown actions
- Ensures no stuck jobs
- Validates orders against printer capabilities
- Checks: page size, color, duplex, max copies
- Skips incompatible orders
- Validates files before printing
- Checks: existence, size, PDF header
- Rejects invalid files safely
- Tracks metrics: prints/hour, failures/hour, delays, offline duration
- Stores metrics in MongoDB every 5 minutes
- Viewable in admin UI
- Helps identify performance issues
- Install on Windows machine with printer
- Configure MongoDB connection
- Set printer name
- Run as service (PM2 recommended)
- Deploy to Vercel/cloud
- Ensure MongoDB connection string is set
- Run migration script to add
printStatusto existing orders
Run migration script to:
- Add
printStatusfield to existing orders - Set
printStatus: 'pending'for completed orders - Set
printStatus: 'printed'for delivered orders - Create required indexes
- Initialize printer records
npm run migrate-print-status- Orders pending/printing/printed
- Printer online/offline status
- Queue lengths
- Error rates
- Processing times
- All state transitions logged
- Admin actions logged
- Errors logged with context
- Stuck order detection logged
- Support for multiple printers
- Priority queue
- Print job scheduling
- Advanced error recovery
- Print preview
- Batch printing