135 lines
3.8 KiB
JavaScript
135 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const sql = require('mssql');
|
|
const database = require('./database');
|
|
|
|
const CONFIG_NAME = 'shujuku2_report_replica';
|
|
const CONFIG_CACHE_TTL_MS = 30000;
|
|
|
|
let pool = null;
|
|
let poolPromise = null;
|
|
let poolSignature = '';
|
|
let cachedConfig = null;
|
|
let configPromise = null;
|
|
let configExpiresAt = 0;
|
|
|
|
async function loadConfig() {
|
|
if (cachedConfig && Date.now() < configExpiresAt) return cachedConfig;
|
|
if (configPromise) return configPromise;
|
|
|
|
configPromise = database.local.query(
|
|
`SELECT host, port, database_name, username, password, \`ssl\`, status
|
|
FROM database_configs
|
|
WHERE name = ?
|
|
LIMIT 1`,
|
|
[CONFIG_NAME]
|
|
).then((rows) => {
|
|
const stored = rows[0];
|
|
if (stored) {
|
|
cachedConfig = stored.status === 'inactive'
|
|
? { disabled: true }
|
|
: {
|
|
server: stored.host,
|
|
port: Number(stored.port || 1433),
|
|
database: stored.database_name,
|
|
user: stored.username,
|
|
password: stored.password,
|
|
encrypt: stored.ssl === 1 || stored.ssl === true,
|
|
};
|
|
} else {
|
|
cachedConfig = {
|
|
server: process.env.CLOUD_DB_REPORT_HOST || process.env.CLOUD_DB_PUBLIC_HOST,
|
|
port: Number(process.env.CLOUD_DB_REPORT_PORT || process.env.CLOUD_DB_PUBLIC_PORT || 1433),
|
|
database: process.env.CLOUD_DB_REPORT_DATABASE || process.env.CLOUD_DB_PUBLIC_NAME || 'JSReportReplica',
|
|
user: process.env.CLOUD_DB_REPORT_USER,
|
|
password: process.env.CLOUD_DB_REPORT_PASSWORD,
|
|
encrypt: String(process.env.CLOUD_DB_REPORT_SSL || process.env.CLOUD_DB_PUBLIC_SSL || '').toLowerCase() === 'true',
|
|
};
|
|
}
|
|
|
|
configExpiresAt = Date.now() + CONFIG_CACHE_TTL_MS;
|
|
return cachedConfig;
|
|
}).finally(() => {
|
|
configPromise = null;
|
|
});
|
|
|
|
return configPromise;
|
|
}
|
|
|
|
function createPoolConfig(config) {
|
|
return {
|
|
server: config.server,
|
|
port: config.port,
|
|
database: config.database,
|
|
user: config.user,
|
|
password: config.password,
|
|
options: {
|
|
encrypt: config.encrypt,
|
|
trustServerCertificate: true,
|
|
},
|
|
requestTimeout: 120000,
|
|
connectionTimeout: 120000,
|
|
pool: {
|
|
max: 8,
|
|
min: 0,
|
|
idleTimeoutMillis: 120000,
|
|
},
|
|
};
|
|
}
|
|
|
|
async function getPool() {
|
|
const config = await loadConfig();
|
|
if (config.disabled) throw new Error('报表副本数据库配置已停用');
|
|
if (!config.server || !config.database || !config.user || !config.password) {
|
|
throw new Error('报表副本数据库配置不完整');
|
|
}
|
|
|
|
const signature = [config.server, config.port, config.database, config.user, config.password, config.encrypt].join('|');
|
|
if (pool?.connected && signature === poolSignature) return pool;
|
|
if (poolPromise && signature === poolSignature) return poolPromise;
|
|
if (pool) await pool.close().catch(() => {});
|
|
|
|
poolSignature = signature;
|
|
poolPromise = new sql.ConnectionPool(createPoolConfig(config)).connect()
|
|
.then((connectedPool) => {
|
|
pool = connectedPool;
|
|
return connectedPool;
|
|
})
|
|
.catch((error) => {
|
|
pool = null;
|
|
poolSignature = '';
|
|
throw error;
|
|
})
|
|
.finally(() => {
|
|
poolPromise = null;
|
|
});
|
|
|
|
return poolPromise;
|
|
}
|
|
|
|
async function query(sqlText, params = {}, options = {}) {
|
|
const activePool = await getPool();
|
|
const request = activePool.request();
|
|
if (options.requestTimeout) request.timeout = Number(options.requestTimeout);
|
|
Object.entries(params).forEach(([key, value]) => request.input(key, value));
|
|
const result = await request.query(sqlText);
|
|
return result.recordset || [];
|
|
}
|
|
|
|
async function close() {
|
|
if (poolPromise) await poolPromise.catch(() => {});
|
|
if (pool) await pool.close().catch(() => {});
|
|
pool = null;
|
|
poolPromise = null;
|
|
poolSignature = '';
|
|
cachedConfig = null;
|
|
configPromise = null;
|
|
configExpiresAt = 0;
|
|
}
|
|
|
|
module.exports = {
|
|
CONFIG_NAME,
|
|
query,
|
|
close,
|
|
};
|