qinglong/shell/preload/client.js
Copilot 214241797d
Fix QlPort and QlGrpcPort environment variables ignored in host network mode and pm2 reload (#2825)
* Initial plan

* Fix host mode port configuration by using QlPort environment variable

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Fix GRPC_PORT conflict in host network mode

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Ensure BACK_PORT and GRPC_PORT survive pm2 reload with --update-env

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Move env.sh sourcing after fix_config to preserve more environment variables

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Refactor: Extract export_ql_envs function and move env.sh sourcing earlier

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

* Extract load_ql_envs function and reorder initialization in docker-entrypoint.sh and update.sh

Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
2025-11-14 22:56:39 +08:00

141 lines
3.1 KiB
JavaScript

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const { join } = require('path');
class GrpcClient {
static #config = {
protoPath: join(process.env.QL_DIR, 'back/protos/api.proto'),
serverAddress: `0.0.0.0:${process.env.GRPC_PORT || '5500'}`,
protoOptions: {
keepCase: true,
longs: String,
enums: String,
defaults: true,
},
grpcOptions: {
'grpc.enable_http_proxy': 0,
},
defaultTimeout: 30000,
};
static #methods = [
'getEnvs',
'createEnv',
'updateEnv',
'deleteEnvs',
'moveEnv',
'disableEnvs',
'enableEnvs',
'updateEnvNames',
'getEnvById',
'systemNotify',
'getCronDetail',
'createCron',
'updateCron',
'deleteCrons',
];
#client;
#api = {};
constructor() {
this.#initializeClient();
this.#bindMethods();
}
#initializeClient() {
try {
const { protoPath, protoOptions, serverAddress, grpcOptions } =
GrpcClient.#config;
const packageDefinition = protoLoader.loadSync(protoPath, protoOptions);
const apiProto = grpc.loadPackageDefinition(packageDefinition).com.ql.api;
this.#client = new apiProto.Api(
serverAddress,
grpc.credentials.createInsecure(),
grpcOptions,
);
} catch (error) {
console.error('Failed to initialize gRPC client:', error);
process.exit(1);
}
}
#promisifyMethod(methodName) {
const capitalizedMethod =
methodName.charAt(0).toUpperCase() + methodName.slice(1);
const method = this.#client[capitalizedMethod].bind(this.#client);
return async (params = {}) => {
return new Promise((resolve, reject) => {
const metadata = new grpc.Metadata();
const deadline = new Date(
Date.now() + GrpcClient.#config.defaultTimeout,
);
method(params, metadata, { deadline }, (error, response) => {
if (error) {
return reject(error);
}
resolve(response);
});
});
};
}
#bindMethods() {
GrpcClient.#methods.forEach((method) => {
this.#api[method] = this.#promisifyMethod(method);
});
}
getApi() {
return {
...this.#api,
close: this.close.bind(this),
};
}
close() {
if (this.#client) {
this.#client.close();
this.#client = null;
}
}
}
const grpcClient = new GrpcClient();
process.on('SIGTERM', () => {
grpcClient.close();
process.exit(0);
});
process.on('SIGINT', () => {
grpcClient.close();
process.exit(0);
});
process.on('unhandledRejection', (reason, promise) => {
if (reason instanceof Error) {
if (reason.stack) {
const relevantStack = reason.stack
.split('\n')
.filter((line) => {
return (
!line.includes('node:internal') &&
!line.includes('node_modules/@grpc') &&
!line.includes('processTicksAndRejections')
);
})
.join('\n');
console.error(relevantStack);
}
} else {
console.error(reason);
}
});
module.exports = grpcClient.getApi();