updated
This commit is contained in:
@@ -1,47 +1,94 @@
|
||||
/**
|
||||
* Quality Label Printing Service - Background Script
|
||||
* Handles communication between web pages and Windows print service
|
||||
* Quality Label Printing Extension - Windows Service Communication
|
||||
* Communicates with local Windows print service for silent printing
|
||||
*/
|
||||
|
||||
// Configuration
|
||||
const PRINT_SERVICE_URL = 'http://localhost:8765';
|
||||
const SERVICE_CHECK_INTERVAL = 30000; // 30 seconds
|
||||
console.log('Quality Label Printing Extension - Windows Service Mode');
|
||||
|
||||
// Service status
|
||||
let serviceStatus = {
|
||||
available: false,
|
||||
lastCheck: null,
|
||||
printers: []
|
||||
};
|
||||
// Service configuration
|
||||
const PRINT_SERVICE_URL = 'http://localhost:8765';
|
||||
const SERVICE_TIMEOUT = 30000; // 30 seconds
|
||||
|
||||
// Initialize extension
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log('Quality Label Printing Service extension installed');
|
||||
checkServiceStatus();
|
||||
|
||||
// Set up periodic service check
|
||||
setInterval(checkServiceStatus, SERVICE_CHECK_INTERVAL);
|
||||
console.log('Quality Label Printing Service extension installed - Windows Service Mode');
|
||||
testServiceConnection();
|
||||
});
|
||||
|
||||
// Handle messages from content scripts or web pages
|
||||
// Test connection to Windows service
|
||||
async function testServiceConnection() {
|
||||
try {
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/health`, {
|
||||
method: 'GET',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ Windows Print Service connected:', data);
|
||||
return true;
|
||||
} else {
|
||||
console.warn('⚠️ Windows Print Service not responding:', response.status);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('❌ Windows Print Service not available:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle messages from content scripts or web pages
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
console.log('Background script received message:', message);
|
||||
|
||||
switch (message.action) {
|
||||
case 'print_pdf':
|
||||
handlePrintPDF(message.data).then(sendResponse);
|
||||
handleWindowsServicePrint(message)
|
||||
.then(result => {
|
||||
console.log('Windows service print completed:', result);
|
||||
sendResponse(result);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Windows service print error:', error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
});
|
||||
return true; // Keep message channel open for async response
|
||||
|
||||
case 'silent_print':
|
||||
handleSilentPrint(message.data).then(sendResponse);
|
||||
return true;
|
||||
|
||||
case 'get_printers':
|
||||
handleGetPrinters().then(sendResponse);
|
||||
getAvailablePrinters()
|
||||
.then(printers => {
|
||||
sendResponse({ success: true, printers: printers });
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error getting printers:', error);
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error.message,
|
||||
printers: [{ name: 'default', display_name: 'Default Printer', is_default: true }]
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
case 'check_service':
|
||||
checkServiceStatus().then(sendResponse);
|
||||
case 'ping':
|
||||
testServiceConnection()
|
||||
.then(connected => {
|
||||
sendResponse({
|
||||
success: true,
|
||||
extension_version: chrome.runtime.getManifest().version,
|
||||
ready: true,
|
||||
service_connected: connected,
|
||||
mode: 'windows_service'
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
sendResponse({
|
||||
success: true,
|
||||
extension_version: chrome.runtime.getManifest().version,
|
||||
ready: false,
|
||||
service_connected: false,
|
||||
mode: 'windows_service'
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -58,10 +105,13 @@ chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) =>
|
||||
'http://localhost:5000',
|
||||
'http://localhost:8000',
|
||||
'http://127.0.0.1:5000',
|
||||
'http://127.0.0.1:8000'
|
||||
'http://127.0.0.1:8000',
|
||||
'http://localhost:3000',
|
||||
'http://localhost:8080'
|
||||
];
|
||||
|
||||
if (!allowedOrigins.includes(sender.origin)) {
|
||||
if (!allowedOrigins.some(origin => sender.url && sender.url.startsWith(origin))) {
|
||||
console.warn('Unauthorized origin:', sender.url);
|
||||
sendResponse({ error: 'Unauthorized origin', success: false });
|
||||
return;
|
||||
}
|
||||
@@ -69,21 +119,35 @@ chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) =>
|
||||
// Handle the message
|
||||
switch (message.action) {
|
||||
case 'print_pdf':
|
||||
case 'silent_print':
|
||||
handleSilentPrint(message.data).then(sendResponse);
|
||||
return true;
|
||||
|
||||
case 'get_printers':
|
||||
handleGetPrinters().then(sendResponse);
|
||||
handleWindowsServicePrint(message)
|
||||
.then(result => sendResponse(result))
|
||||
.catch(error => {
|
||||
console.error('Print PDF error:', error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
});
|
||||
return true;
|
||||
|
||||
case 'ping':
|
||||
sendResponse({
|
||||
success: true,
|
||||
service_available: serviceStatus.available,
|
||||
extension_version: chrome.runtime.getManifest().version
|
||||
});
|
||||
break;
|
||||
testServiceConnection()
|
||||
.then(connected => {
|
||||
sendResponse({
|
||||
success: true,
|
||||
extension_version: chrome.runtime.getManifest().version,
|
||||
ready: true,
|
||||
service_connected: connected,
|
||||
mode: 'windows_service'
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
sendResponse({
|
||||
success: true,
|
||||
extension_version: chrome.runtime.getManifest().version,
|
||||
ready: false,
|
||||
service_connected: false,
|
||||
mode: 'windows_service'
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
default:
|
||||
sendResponse({ error: 'Unknown action', success: false });
|
||||
@@ -91,209 +155,153 @@ chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) =>
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the Windows print service is available
|
||||
* Get available printers from Windows service
|
||||
*/
|
||||
async function checkServiceStatus() {
|
||||
async function getAvailablePrinters() {
|
||||
try {
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/health`, {
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/printers`, {
|
||||
method: 'GET',
|
||||
timeout: 5000
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
serviceStatus.available = true;
|
||||
serviceStatus.lastCheck = new Date();
|
||||
|
||||
console.log('Print service is available:', data);
|
||||
|
||||
// Update extension badge
|
||||
chrome.action.setBadgeText({ text: '✓' });
|
||||
chrome.action.setBadgeBackgroundColor({ color: '#28a745' });
|
||||
|
||||
return { success: true, status: data };
|
||||
return data.printers || [];
|
||||
} else {
|
||||
throw new Error(`Service returned status ${response.status}`);
|
||||
throw new Error(`Service responded with status: ${response.status}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.warn('Print service not available:', error);
|
||||
|
||||
serviceStatus.available = false;
|
||||
serviceStatus.lastCheck = new Date();
|
||||
|
||||
// Update extension badge
|
||||
chrome.action.setBadgeText({ text: '✗' });
|
||||
chrome.action.setBadgeBackgroundColor({ color: '#dc3545' });
|
||||
|
||||
return { success: false, error: error.message };
|
||||
console.error('Failed to get printers from service:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PDF printing request
|
||||
* Handle PDF printing via Windows Service
|
||||
*/
|
||||
async function handlePrintPDF(printData) {
|
||||
async function handleWindowsServicePrint(message) {
|
||||
console.log('🖨️ Sending PDF to Windows print service:', message);
|
||||
|
||||
try {
|
||||
if (!serviceStatus.available) {
|
||||
await checkServiceStatus();
|
||||
|
||||
if (!serviceStatus.available) {
|
||||
throw new Error('Print service is not available');
|
||||
}
|
||||
}
|
||||
const { pdfUrl, orderId, prodOrder, quantity, printerName } = message;
|
||||
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/print/pdf`, {
|
||||
if (!pdfUrl) {
|
||||
throw new Error('PDF URL is required');
|
||||
}
|
||||
|
||||
// First, test if service is available
|
||||
const serviceAvailable = await testServiceConnection();
|
||||
if (!serviceAvailable) {
|
||||
throw new Error('Windows Print Service is not running. Please ensure the service is installed and started.');
|
||||
}
|
||||
|
||||
// Prepare print request
|
||||
const printRequest = {
|
||||
pdf_url: pdfUrl,
|
||||
printer_name: printerName || 'default',
|
||||
order_id: orderId,
|
||||
prod_order: prodOrder,
|
||||
quantity: quantity,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
console.log('📤 Sending print request to service:', printRequest);
|
||||
|
||||
// Send PDF to Windows service for printing
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/print_pdf`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(printData)
|
||||
body: JSON.stringify(printRequest)
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Print service error (${response.status}): ${errorText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('Print job completed:', result);
|
||||
if (result.success) {
|
||||
console.log('✅ Print job sent successfully:', result);
|
||||
|
||||
// Show notification
|
||||
chrome.notifications.create({
|
||||
type: 'basic',
|
||||
iconUrl: 'icons/icon48.png',
|
||||
title: 'Quality Label Printing Service',
|
||||
message: 'PDF printed successfully'
|
||||
});
|
||||
|
||||
return { success: true, result };
|
||||
} else {
|
||||
throw new Error(result.error || 'Print job failed');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Print PDF error:', error);
|
||||
|
||||
// Show error notification
|
||||
chrome.notifications.create({
|
||||
type: 'basic',
|
||||
iconUrl: 'icons/icon48.png',
|
||||
title: 'Quality Label Printing Service',
|
||||
message: `Print failed: ${error.message}`
|
||||
});
|
||||
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle silent printing request
|
||||
*/
|
||||
async function handleSilentPrint(printData) {
|
||||
try {
|
||||
if (!serviceStatus.available) {
|
||||
await checkServiceStatus();
|
||||
|
||||
if (!serviceStatus.available) {
|
||||
// Try direct browser printing as fallback
|
||||
return await handleDirectPrint(printData);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/print/silent`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(printData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('Silent print completed:', result);
|
||||
return { success: true, result };
|
||||
} else {
|
||||
throw new Error(result.error || 'Silent print failed');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Silent print error:', error);
|
||||
|
||||
// Fallback to direct printing
|
||||
return await handleDirectPrint(printData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle direct browser printing (fallback)
|
||||
*/
|
||||
async function handleDirectPrint(printData) {
|
||||
try {
|
||||
// Create hidden iframe for printing
|
||||
const printFrame = document.createElement('iframe');
|
||||
printFrame.style.display = 'none';
|
||||
printFrame.src = printData.pdf_url || 'data:application/pdf;base64,' + printData.pdf_data;
|
||||
|
||||
document.body.appendChild(printFrame);
|
||||
|
||||
// Wait for load and print
|
||||
return new Promise((resolve) => {
|
||||
printFrame.onload = () => {
|
||||
setTimeout(() => {
|
||||
printFrame.contentWindow.print();
|
||||
document.body.removeChild(printFrame);
|
||||
resolve({ success: true, method: 'browser_fallback' });
|
||||
}, 1000);
|
||||
return {
|
||||
success: true,
|
||||
message: `PDF sent to ${result.printer || printerName || 'default printer'} successfully`,
|
||||
method: result.method || 'Windows Print Service',
|
||||
printer: result.printer || printerName,
|
||||
orderId: orderId,
|
||||
instruction: 'PDF has been sent to the printer queue'
|
||||
};
|
||||
});
|
||||
|
||||
} else {
|
||||
throw new Error(result.error || 'Unknown print service error');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Direct print error:', error);
|
||||
return { success: false, error: error.message };
|
||||
console.error('❌ Windows service print failed:', error);
|
||||
|
||||
// Fallback: Return instruction to print manually
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
fallback: true,
|
||||
instruction: 'Windows service unavailable. Please download and print the PDF manually.'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available printers
|
||||
* Fallback function for when Windows service is not available
|
||||
*/
|
||||
async function handleGetPrinters() {
|
||||
async function handleFallbackPrint(message) {
|
||||
console.log('🔄 Using fallback print method:', message);
|
||||
|
||||
try {
|
||||
if (!serviceStatus.available) {
|
||||
await checkServiceStatus();
|
||||
|
||||
if (!serviceStatus.available) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Print service not available',
|
||||
printers: []
|
||||
};
|
||||
}
|
||||
}
|
||||
const { pdfUrl, orderId, prodOrder, quantity } = message;
|
||||
|
||||
const response = await fetch(`${PRINT_SERVICE_URL}/printers`, {
|
||||
method: 'GET'
|
||||
// Create a new tab with the PDF
|
||||
const tab = await chrome.tabs.create({
|
||||
url: pdfUrl,
|
||||
active: false // Don't switch to the tab
|
||||
});
|
||||
|
||||
// Wait a moment for PDF to load
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Get the created tab
|
||||
const updatedTab = await chrome.tabs.get(tab.id);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
serviceStatus.printers = result.printers || [];
|
||||
return { success: true, printers: result.printers };
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to get printers');
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `PDF opened in new tab for manual printing`,
|
||||
method: 'Manual Print Fallback',
|
||||
tabId: tab.id,
|
||||
instruction: 'PDF opened in new tab. Use Ctrl+P to print or close the tab if not needed.'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Get printers error:', error);
|
||||
return { success: false, error: error.message, printers: [] };
|
||||
console.error('❌ Fallback print failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for testing
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
checkServiceStatus,
|
||||
handlePrintPDF,
|
||||
handleSilentPrint,
|
||||
handleGetPrinters
|
||||
};
|
||||
}
|
||||
// Utility functions
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Extension startup
|
||||
console.log('🖨️ Quality Label Printing Extension loaded - Windows Service Mode');
|
||||
console.log(`🔗 Service URL: ${PRINT_SERVICE_URL}`);
|
||||
|
||||
// Test service on startup
|
||||
testServiceConnection().then(connected => {
|
||||
if (connected) {
|
||||
console.log('✅ Windows Print Service is available');
|
||||
} else {
|
||||
console.log('⚠️ Windows Print Service is not available - fallback mode active');
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ Background script loaded successfully - Platform safe mode enabled');
|
||||
@@ -1,28 +1,48 @@
|
||||
/**
|
||||
* Quality Label Printing Service - Content Script
|
||||
* Injects print service functionality into web pages
|
||||
* Simplified injection for extension ID detection
|
||||
*/
|
||||
|
||||
console.log('Quality Label Printing - Content Script Loaded');
|
||||
|
||||
// Inject extension ID into the page as a hidden DOM element for detection
|
||||
// Inject extension ID into DOM for web page detection
|
||||
function injectExtensionId() {
|
||||
try {
|
||||
const existing = document.getElementById('chrome-extension-id');
|
||||
if (existing) return; // Already injected
|
||||
const el = document.createElement('div');
|
||||
el.id = 'chrome-extension-id';
|
||||
el.setAttribute('data-extension-id', chrome.runtime.id);
|
||||
el.style.display = 'none';
|
||||
document.documentElement.appendChild(el);
|
||||
// Optionally, also set a global variable
|
||||
window.CHROME_EXTENSION_ID = chrome.runtime.id;
|
||||
console.log('Injected extension ID:', chrome.runtime.id);
|
||||
} catch (e) {
|
||||
console.warn('Failed to inject extension ID:', e);
|
||||
// Remove any existing extension ID element
|
||||
const existingElement = document.getElementById('chrome-extension-id');
|
||||
if (existingElement) {
|
||||
existingElement.remove();
|
||||
}
|
||||
|
||||
// Create new element with extension ID
|
||||
const extensionIdElement = document.createElement('div');
|
||||
extensionIdElement.id = 'chrome-extension-id';
|
||||
extensionIdElement.setAttribute('data-extension-id', chrome.runtime.id);
|
||||
extensionIdElement.style.display = 'none';
|
||||
|
||||
// Add to document head or body
|
||||
(document.head || document.body || document.documentElement).appendChild(extensionIdElement);
|
||||
|
||||
console.log('Extension ID injected:', chrome.runtime.id);
|
||||
}
|
||||
injectExtensionId();
|
||||
|
||||
console.log('Quality Label Printing Service: Content script loaded');
|
||||
// Inject extension ID when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', injectExtensionId);
|
||||
} else {
|
||||
injectExtensionId();
|
||||
}
|
||||
|
||||
// Remove all script injection and messaging. Only inject the extension ID for CSP compatibility.
|
||||
// Also inject when page changes (for SPAs)
|
||||
if (window.navigation) {
|
||||
navigation.addEventListener('navigate', injectExtensionId);
|
||||
} else {
|
||||
// Fallback for older browsers
|
||||
let lastUrl = location.href;
|
||||
new MutationObserver(() => {
|
||||
const url = location.href;
|
||||
if (url !== lastUrl) {
|
||||
lastUrl = url;
|
||||
setTimeout(injectExtensionId, 100);
|
||||
}
|
||||
}).observe(document, { subtree: true, childList: true });
|
||||
}
|
||||
@@ -2,23 +2,25 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Quality Label Printing Service",
|
||||
"version": "1.0.0",
|
||||
"description": "Silent PDF printing service for Quality Label Printing application",
|
||||
"description": "Simple silent PDF printing for Quality Label Printing application",
|
||||
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"storage",
|
||||
"nativeMessaging",
|
||||
"printingMetrics"
|
||||
"downloads",
|
||||
"tabs",
|
||||
"scripting"
|
||||
],
|
||||
|
||||
"host_permissions": [
|
||||
"http://localhost:*/*",
|
||||
"https://localhost:*/*"
|
||||
"https://localhost:*/*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
|
||||
"background": {
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
|
||||
"content_scripts": [{
|
||||
|
||||
Reference in New Issue
Block a user