How do I use node-opcua when deploying an app with IIS?

I am trying to read value from an opcua server in my sveltekit app using node-opcua. I can successfully connect and read the values from my dev machine but when I deploy the app on IIS I get the error below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Error: EEXIST: file already exists, mkdir 'C:Windowssystem32configsystemprofileAppDataRoamingnode-opcua-default-nodejsConfigPKI'
at Object.mkdirSync (node:fs:1373:26)
at mkdir (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibtoolboxcommon2.js:48:22)
at new CertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibpkicertificate_manager.js:202:29)
at new OPCUACertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:42:9)
at getDefaultCertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:127:12)
at new ClientBaseImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateclient_base_impl.js:296:114)
at new OPCUAClientImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateopcua_client_impl.js:287:9)
at OPCUAClient.create (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistopcua_client.js:10:16)
at file:///C:/inetpub/AreaTracking/wwwroot/build/server/chunks/_server-bf75dda4.js:58:28
at ModuleJob.run (node:internal/modules/esm/module_job:222:25) {
errno: -4075,
code: 'EEXIST',
syscall: 'mkdir',
path: 'C:\Windows\system32\config\systemprofile\AppData\Roaming\node-opcua-default-nodejs\Config\PKI'
}
</code>
<code>Error: EEXIST: file already exists, mkdir 'C:Windowssystem32configsystemprofileAppDataRoamingnode-opcua-default-nodejsConfigPKI' at Object.mkdirSync (node:fs:1373:26) at mkdir (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibtoolboxcommon2.js:48:22) at new CertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibpkicertificate_manager.js:202:29) at new OPCUACertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:42:9) at getDefaultCertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:127:12) at new ClientBaseImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateclient_base_impl.js:296:114) at new OPCUAClientImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateopcua_client_impl.js:287:9) at OPCUAClient.create (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistopcua_client.js:10:16) at file:///C:/inetpub/AreaTracking/wwwroot/build/server/chunks/_server-bf75dda4.js:58:28 at ModuleJob.run (node:internal/modules/esm/module_job:222:25) { errno: -4075, code: 'EEXIST', syscall: 'mkdir', path: 'C:\Windows\system32\config\systemprofile\AppData\Roaming\node-opcua-default-nodejs\Config\PKI' } </code>
Error: EEXIST: file already exists, mkdir 'C:Windowssystem32configsystemprofileAppDataRoamingnode-opcua-default-nodejsConfigPKI'
    at Object.mkdirSync (node:fs:1373:26)
    at mkdir (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibtoolboxcommon2.js:48:22)
    at new CertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibpkicertificate_manager.js:202:29)
    at new OPCUACertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:42:9)
    at getDefaultCertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:127:12)
    at new ClientBaseImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateclient_base_impl.js:296:114)
    at new OPCUAClientImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateopcua_client_impl.js:287:9)
    at OPCUAClient.create (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistopcua_client.js:10:16)
    at file:///C:/inetpub/AreaTracking/wwwroot/build/server/chunks/_server-bf75dda4.js:58:28
    at ModuleJob.run (node:internal/modules/esm/module_job:222:25) {
  errno: -4075,
  code: 'EEXIST',
  syscall: 'mkdir',
  path: 'C:\Windows\system32\config\systemprofile\AppData\Roaming\node-opcua-default-nodejs\Config\PKI'
}

Since it is IIS I think it does not have access to this folder. I have tried to save the pki files in the same directory as the +server.js file that is reading these values. But I am still getting the same error. I can see the PKI files are being generated on the server but it does not seem to be reading from the generated files. Here is the +server.js file that is reading these values and genereating the pki files.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import {
OPCUAClient,
AttributeIds,
UserTokenType,
MessageSecurityMode,
SecurityPolicy,
OPCUACertificateManager
} from 'node-opcua';
import * as path from 'path';
import * as fs from 'fs';
const opcServerEndpoint = "endpoint";
const username = "USR";
const password = "PWD";
// Get the directory path of the current module file
const currentDir = new URL('.', import.meta.url).pathname;
// Resolve the pki directory relative to the current module file
const pkiDir = path.resolve(currentDir.slice(1), 'pki');
// Log the PKI directory path for debugging purposes
console.log('PKI directory:', pkiDir);
// Ensure the PKI directory exists or create it if it doesn't
try {
fs.mkdirSync(pkiDir, { recursive: true });
console.log('PKI directory created successfully');
} catch (err) {
console.error('Error creating PKI directory:', err);
}
// Check if certificate files exist, and generate them if not
if (!fs.existsSync(path.join(pkiDir, 'own', 'cert.pem'))) {
console.log('Generating certificate files...');
const certificateManager = new OPCUACertificateManager({
rootFolder: pkiDir, // Specify the root folder for certificate management
keySize: 2048, // Set the key size for certificate generation
// Specify the location for storing generated certificates
// @ts-ignore
location: path.join(pkiDir, 'own'),
});
// Initialize the certificate manager
certificateManager.initialize()
.then(() => console.log('Certificate files generated successfully'))
.catch(err => console.error('Error generating certificate files:', err));
}
// Initialize the OPC UA client options
const clientOptions = {
applicationName: "RejectTrackingClient", // Specify the name of the application
endpointMustExist: true, // Ensure that the specified endpoint exists
securityMode: MessageSecurityMode.SignAndEncrypt, // Set the security mode
securityPolicy: SecurityPolicy.Basic256Sha256, // Set the security policy
connectionStrategy: {
maxRetry: 5, // Maximum number of connection retries
initialDelay: 1000, // Initial delay before the first connection retry (in milliseconds)
maxDelay: 10000 // Maximum delay between connection retries (in milliseconds)
},
// Specify the certificate manager to use for secure communication
certificateManager: new OPCUACertificateManager({
automaticallyAcceptUnknownCertificate: true, // Automatically accept unknown certificates
rootFolder: pkiDir, // Set the root folder for certificate management
keySize: 2048, // Set the key size for certificate generation
// Specify the location for storing generated certificates
// @ts-ignore
location: path.join(pkiDir, 'own'),
}),
};
const client = OPCUAClient.create(clientOptions);
/**
* @type {import("node-opcua").ClientSession | null}
*/
let session = null;
let isConnected = false;
/**
* @type {string | number | NodeJS.Timeout | null | undefined}
*/
let inactivityTimer = null;
async function initializeOpcClient() {
try {
if (!client) {
// Create the OPC UA client with proper security settings
// @ts-ignore
client = OPCUAClient.create({
endpointMustExist: true,
securityMode: MessageSecurityMode.SignAndEncrypt,
securityPolicy: SecurityPolicy.Basic256Sha256,
connectionStrategy: {
maxRetry: 5,
initialDelay: 1000,
maxDelay: 10000
},
// @ts-ignore
certificateManager: new OPCUACertificateManager({
keySize: 2048,
// @ts-ignore
location: pkiDir
})
});
// Enable detailed logging
client.on("backoff", (retry, delay) => {
console.log(`Retrying to connect to ${opcServerEndpoint}, attempt ${retry}, next attempt in ${delay / 1000} seconds`);
});
}
console.log('Connecting to OPC UA server...');
await client.connect(opcServerEndpoint);
console.log('Connected to OPC UA server');
isConnected = true;
// Create a session with username and password
console.log('Creating session...');
session = await client.createSession({
userName: username,
password: password,
type: UserTokenType.UserName
});
console.log('Session created');
} catch (error) {
console.error('Error initializing OPC UA client:', error);
// @ts-ignore
throw new Error(`Failed to initialize OPC UA client: ${error.message}`);
}
}
async function closeOpcClient() {
try {
if (session) {
await session.close();
session = null;
console.log('Session closed');
}
if (client) {
await client.disconnect();
// @ts-ignore
client = null;
console.log('Client disconnected');
}
isConnected = false;
} catch (error) {
console.error('Error closing OPC UA client:', error);
}
}
function resetInactivityTimer() {
if (inactivityTimer) {
clearTimeout(inactivityTimer);
}
inactivityTimer = setTimeout(async () => {
console.log('Inactivity timeout reached, closing connection...');
await closeOpcClient();
}, 25000); // 25 seconds
}
async function readOpcData() {
try {
// Ensure the client and session are initialized and connected
if (!isConnected || !session) {
await initializeOpcClient();
}
// Define an array of nodeIds to read
const nodeIds = [
"ns=2;s=POS_1_ID",
"ns=2;s=POS_1_LEN",
"ns=2;s=POS_1_TCK",
"ns=2;s=POS_1_WIDTH"
];
// Perform read operations for each nodeId
const tagValues = [];
for (const nodeId of nodeIds) {
const nodeToRead = {
nodeId: nodeId,
attributeId: AttributeIds.Value,
};
// @ts-ignore
const dataValue = await session.read(nodeToRead);
// Convert Int8Array to string if the value is an array of numbers
if (Array.isArray(dataValue.value.value) && dataValue.value.value.every(val => typeof val === 'number')) {
const stringValue = dataValue.value.value.map(val => String.fromCharCode(val)).join('');
tagValues.push(stringValue);
} else {
tagValues.push(dataValue.value.value);
}
}
return tagValues;
} catch (error) {
console.error('Error in readOpcData:', error);
// @ts-ignore
throw new Error(`Failed to read OPC data: ${error.message}`);
} finally {
// Reset the inactivity timer after a successful read
resetInactivityTimer();
}
}
export async function GET() {
try {
const data = await readOpcData();
console.log(data);
return new Response(JSON.stringify({ data }), { status: 200 });
} catch (error) {
console.error('Error in GET handler:', error);
if (error instanceof Error) {
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
} else {
return new Response(JSON.stringify({ error: "An unknown error occurred" }), { status: 500 });
}
}
}
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Received SIGINT, shutting down gracefully...');
try {
await closeOpcClient();
} catch (error) {
console.error('Error during shutdown:', error);
} finally {
process.exit(0);
}
});
process.on('SIGTERM', async () => {
console.log('Received SIGTERM, shutting down gracefully...');
try {
await closeOpcClient();
} catch (error) {
console.error('Error during shutdown:', error);
} finally {
process.exit(0);
}
});
</code>
<code>import { OPCUAClient, AttributeIds, UserTokenType, MessageSecurityMode, SecurityPolicy, OPCUACertificateManager } from 'node-opcua'; import * as path from 'path'; import * as fs from 'fs'; const opcServerEndpoint = "endpoint"; const username = "USR"; const password = "PWD"; // Get the directory path of the current module file const currentDir = new URL('.', import.meta.url).pathname; // Resolve the pki directory relative to the current module file const pkiDir = path.resolve(currentDir.slice(1), 'pki'); // Log the PKI directory path for debugging purposes console.log('PKI directory:', pkiDir); // Ensure the PKI directory exists or create it if it doesn't try { fs.mkdirSync(pkiDir, { recursive: true }); console.log('PKI directory created successfully'); } catch (err) { console.error('Error creating PKI directory:', err); } // Check if certificate files exist, and generate them if not if (!fs.existsSync(path.join(pkiDir, 'own', 'cert.pem'))) { console.log('Generating certificate files...'); const certificateManager = new OPCUACertificateManager({ rootFolder: pkiDir, // Specify the root folder for certificate management keySize: 2048, // Set the key size for certificate generation // Specify the location for storing generated certificates // @ts-ignore location: path.join(pkiDir, 'own'), }); // Initialize the certificate manager certificateManager.initialize() .then(() => console.log('Certificate files generated successfully')) .catch(err => console.error('Error generating certificate files:', err)); } // Initialize the OPC UA client options const clientOptions = { applicationName: "RejectTrackingClient", // Specify the name of the application endpointMustExist: true, // Ensure that the specified endpoint exists securityMode: MessageSecurityMode.SignAndEncrypt, // Set the security mode securityPolicy: SecurityPolicy.Basic256Sha256, // Set the security policy connectionStrategy: { maxRetry: 5, // Maximum number of connection retries initialDelay: 1000, // Initial delay before the first connection retry (in milliseconds) maxDelay: 10000 // Maximum delay between connection retries (in milliseconds) }, // Specify the certificate manager to use for secure communication certificateManager: new OPCUACertificateManager({ automaticallyAcceptUnknownCertificate: true, // Automatically accept unknown certificates rootFolder: pkiDir, // Set the root folder for certificate management keySize: 2048, // Set the key size for certificate generation // Specify the location for storing generated certificates // @ts-ignore location: path.join(pkiDir, 'own'), }), }; const client = OPCUAClient.create(clientOptions); /** * @type {import("node-opcua").ClientSession | null} */ let session = null; let isConnected = false; /** * @type {string | number | NodeJS.Timeout | null | undefined} */ let inactivityTimer = null; async function initializeOpcClient() { try { if (!client) { // Create the OPC UA client with proper security settings // @ts-ignore client = OPCUAClient.create({ endpointMustExist: true, securityMode: MessageSecurityMode.SignAndEncrypt, securityPolicy: SecurityPolicy.Basic256Sha256, connectionStrategy: { maxRetry: 5, initialDelay: 1000, maxDelay: 10000 }, // @ts-ignore certificateManager: new OPCUACertificateManager({ keySize: 2048, // @ts-ignore location: pkiDir }) }); // Enable detailed logging client.on("backoff", (retry, delay) => { console.log(`Retrying to connect to ${opcServerEndpoint}, attempt ${retry}, next attempt in ${delay / 1000} seconds`); }); } console.log('Connecting to OPC UA server...'); await client.connect(opcServerEndpoint); console.log('Connected to OPC UA server'); isConnected = true; // Create a session with username and password console.log('Creating session...'); session = await client.createSession({ userName: username, password: password, type: UserTokenType.UserName }); console.log('Session created'); } catch (error) { console.error('Error initializing OPC UA client:', error); // @ts-ignore throw new Error(`Failed to initialize OPC UA client: ${error.message}`); } } async function closeOpcClient() { try { if (session) { await session.close(); session = null; console.log('Session closed'); } if (client) { await client.disconnect(); // @ts-ignore client = null; console.log('Client disconnected'); } isConnected = false; } catch (error) { console.error('Error closing OPC UA client:', error); } } function resetInactivityTimer() { if (inactivityTimer) { clearTimeout(inactivityTimer); } inactivityTimer = setTimeout(async () => { console.log('Inactivity timeout reached, closing connection...'); await closeOpcClient(); }, 25000); // 25 seconds } async function readOpcData() { try { // Ensure the client and session are initialized and connected if (!isConnected || !session) { await initializeOpcClient(); } // Define an array of nodeIds to read const nodeIds = [ "ns=2;s=POS_1_ID", "ns=2;s=POS_1_LEN", "ns=2;s=POS_1_TCK", "ns=2;s=POS_1_WIDTH" ]; // Perform read operations for each nodeId const tagValues = []; for (const nodeId of nodeIds) { const nodeToRead = { nodeId: nodeId, attributeId: AttributeIds.Value, }; // @ts-ignore const dataValue = await session.read(nodeToRead); // Convert Int8Array to string if the value is an array of numbers if (Array.isArray(dataValue.value.value) && dataValue.value.value.every(val => typeof val === 'number')) { const stringValue = dataValue.value.value.map(val => String.fromCharCode(val)).join(''); tagValues.push(stringValue); } else { tagValues.push(dataValue.value.value); } } return tagValues; } catch (error) { console.error('Error in readOpcData:', error); // @ts-ignore throw new Error(`Failed to read OPC data: ${error.message}`); } finally { // Reset the inactivity timer after a successful read resetInactivityTimer(); } } export async function GET() { try { const data = await readOpcData(); console.log(data); return new Response(JSON.stringify({ data }), { status: 200 }); } catch (error) { console.error('Error in GET handler:', error); if (error instanceof Error) { return new Response(JSON.stringify({ error: error.message }), { status: 500 }); } else { return new Response(JSON.stringify({ error: "An unknown error occurred" }), { status: 500 }); } } } // Graceful shutdown process.on('SIGINT', async () => { console.log('Received SIGINT, shutting down gracefully...'); try { await closeOpcClient(); } catch (error) { console.error('Error during shutdown:', error); } finally { process.exit(0); } }); process.on('SIGTERM', async () => { console.log('Received SIGTERM, shutting down gracefully...'); try { await closeOpcClient(); } catch (error) { console.error('Error during shutdown:', error); } finally { process.exit(0); } }); </code>
import {
    OPCUAClient,
    AttributeIds,
    UserTokenType,
    MessageSecurityMode,
    SecurityPolicy,
    OPCUACertificateManager
} from 'node-opcua';
import * as path from 'path';
import * as fs from 'fs';

const opcServerEndpoint = "endpoint";
const username = "USR";
const password = "PWD";

// Get the directory path of the current module file
const currentDir = new URL('.', import.meta.url).pathname;

// Resolve the pki directory relative to the current module file
const pkiDir = path.resolve(currentDir.slice(1), 'pki');

// Log the PKI directory path for debugging purposes
console.log('PKI directory:', pkiDir);

// Ensure the PKI directory exists or create it if it doesn't
try {
    fs.mkdirSync(pkiDir, { recursive: true });
    console.log('PKI directory created successfully');
} catch (err) {
    console.error('Error creating PKI directory:', err);
}

// Check if certificate files exist, and generate them if not
if (!fs.existsSync(path.join(pkiDir, 'own', 'cert.pem'))) {
    console.log('Generating certificate files...');
    const certificateManager = new OPCUACertificateManager({
        rootFolder: pkiDir, // Specify the root folder for certificate management
        keySize: 2048, // Set the key size for certificate generation
        // Specify the location for storing generated certificates
        // @ts-ignore
        location: path.join(pkiDir, 'own'),
    });
    // Initialize the certificate manager
    certificateManager.initialize()
        .then(() => console.log('Certificate files generated successfully'))
        .catch(err => console.error('Error generating certificate files:', err));
}

// Initialize the OPC UA client options
const clientOptions = {
    applicationName: "RejectTrackingClient", // Specify the name of the application
    endpointMustExist: true, // Ensure that the specified endpoint exists
    securityMode: MessageSecurityMode.SignAndEncrypt, // Set the security mode
    securityPolicy: SecurityPolicy.Basic256Sha256, // Set the security policy
    connectionStrategy: {
        maxRetry: 5, // Maximum number of connection retries
        initialDelay: 1000, // Initial delay before the first connection retry (in milliseconds)
        maxDelay: 10000 // Maximum delay between connection retries (in milliseconds)
    },
    // Specify the certificate manager to use for secure communication
    certificateManager: new OPCUACertificateManager({
        automaticallyAcceptUnknownCertificate: true, // Automatically accept unknown certificates
        rootFolder: pkiDir, // Set the root folder for certificate management
        keySize: 2048, // Set the key size for certificate generation
        // Specify the location for storing generated certificates
        // @ts-ignore
        location: path.join(pkiDir, 'own'),
    }),
};

const client = OPCUAClient.create(clientOptions);

/**
 * @type {import("node-opcua").ClientSession | null}
 */
let session = null;
let isConnected = false;
/**
 * @type {string | number | NodeJS.Timeout | null | undefined}
 */
let inactivityTimer = null;

async function initializeOpcClient() {
    try {
        if (!client) {
            // Create the OPC UA client with proper security settings
            // @ts-ignore
            client = OPCUAClient.create({
                endpointMustExist: true,
                securityMode: MessageSecurityMode.SignAndEncrypt,
                securityPolicy: SecurityPolicy.Basic256Sha256,
                connectionStrategy: {
                    maxRetry: 5,
                    initialDelay: 1000,
                    maxDelay: 10000
                },
                // @ts-ignore
                certificateManager: new OPCUACertificateManager({
                    keySize: 2048,
                    // @ts-ignore
                    location: pkiDir
                })
            });

            // Enable detailed logging
            client.on("backoff", (retry, delay) => {
                console.log(`Retrying to connect to ${opcServerEndpoint}, attempt ${retry}, next attempt in ${delay / 1000} seconds`);
            });
        }

        console.log('Connecting to OPC UA server...');
        await client.connect(opcServerEndpoint);
        console.log('Connected to OPC UA server');
        isConnected = true;

        // Create a session with username and password
        console.log('Creating session...');
        session = await client.createSession({
            userName: username,
            password: password,
            type: UserTokenType.UserName
        });
        console.log('Session created');
    } catch (error) {
        console.error('Error initializing OPC UA client:', error);
        // @ts-ignore
        throw new Error(`Failed to initialize OPC UA client: ${error.message}`);
    }
}


async function closeOpcClient() {
    try {
        if (session) {
            await session.close();
            session = null;
            console.log('Session closed');
        }
        if (client) {
            await client.disconnect();
            // @ts-ignore
            client = null;
            console.log('Client disconnected');
        }
        isConnected = false;
    } catch (error) {
        console.error('Error closing OPC UA client:', error);
    }
}

function resetInactivityTimer() {
    if (inactivityTimer) {
        clearTimeout(inactivityTimer);
    }
    inactivityTimer = setTimeout(async () => {
        console.log('Inactivity timeout reached, closing connection...');
        await closeOpcClient();
    }, 25000); // 25 seconds
}

async function readOpcData() {
    try {
        // Ensure the client and session are initialized and connected
        if (!isConnected || !session) {
            await initializeOpcClient();
        }

        // Define an array of nodeIds to read
        const nodeIds = [
            "ns=2;s=POS_1_ID",
            "ns=2;s=POS_1_LEN",
            "ns=2;s=POS_1_TCK",
            "ns=2;s=POS_1_WIDTH"
        ];

        // Perform read operations for each nodeId
        const tagValues = [];
        for (const nodeId of nodeIds) {
            const nodeToRead = {
                nodeId: nodeId,
                attributeId: AttributeIds.Value,
            };
            // @ts-ignore
            const dataValue = await session.read(nodeToRead);

            // Convert Int8Array to string if the value is an array of numbers
            if (Array.isArray(dataValue.value.value) && dataValue.value.value.every(val => typeof val === 'number')) {
                const stringValue = dataValue.value.value.map(val => String.fromCharCode(val)).join('');
                tagValues.push(stringValue);
            } else {
                tagValues.push(dataValue.value.value);
            }
        }

        return tagValues;
    } catch (error) {
        console.error('Error in readOpcData:', error);
        // @ts-ignore
        throw new Error(`Failed to read OPC data: ${error.message}`);
    } finally {
        // Reset the inactivity timer after a successful read
        resetInactivityTimer();
    }
}

export async function GET() {
    try {
        const data = await readOpcData();
        console.log(data);
        return new Response(JSON.stringify({ data }), { status: 200 });
    } catch (error) {
        console.error('Error in GET handler:', error);
        if (error instanceof Error) {
            return new Response(JSON.stringify({ error: error.message }), { status: 500 });
        } else {
            return new Response(JSON.stringify({ error: "An unknown error occurred" }), { status: 500 });
        }
    }
}

// Graceful shutdown
process.on('SIGINT', async () => {
    console.log('Received SIGINT, shutting down gracefully...');
    try {
        await closeOpcClient();
    } catch (error) {
        console.error('Error during shutdown:', error);
    } finally {
        process.exit(0);
    }
});

process.on('SIGTERM', async () => {
    console.log('Received SIGTERM, shutting down gracefully...');
    try {
        await closeOpcClient();
    } catch (error) {
        console.error('Error during shutdown:', error);
    } finally {
        process.exit(0);
    }
});

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật