| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- 'use strict';
- /**
- * ops 存储门面
- * ----------------------------------------------------------------------------
- * 读 config.storage 决定驱动(默认 SQLite),把驱动实例藏起来,对外只暴露业务接口。
- * 关键约束:初始化失败不能拖垮主服务 —— 所有失败都收敛成 health() 的 ok:false,
- * 只有「店铺运营」相关接口会返回 503。
- */
- const path = require('path');
- const ROOT_DIR = __dirname;
- function pickDriver(storage) {
- const raw = String((storage && storage.driver) || 'sqlite').toLowerCase();
- return raw === 'postgres' || raw === 'postgresql' || raw === 'pg' ? 'postgres' : 'sqlite';
- }
- function createOpsStore(config) {
- const storage = (config && config.storage) || {};
- const driver = pickDriver(storage);
- let impl = null;
- let initPromise = null;
- let lastFailure = null;
- function build() {
- if (driver === 'postgres') {
- const pgCfg = storage.postgres || {};
- const connectionString = process.env.OPS_DATABASE_URL || pgCfg.connectionString || '';
- if (!String(connectionString).trim()) {
- const err = new Error('未配置 PostgreSQL 连接串');
- err.reason = 'storage-not-configured';
- throw err;
- }
- const { createPgStore } = require('./server/stores/ops-pg.js');
- return createPgStore({ ...pgCfg, connectionString });
- }
- const sqliteCfg = storage.sqlite || {};
- const file = sqliteCfg.file
- ? path.resolve(ROOT_DIR, String(sqliteCfg.file))
- : path.join(ROOT_DIR, 'data', 'ops.db');
- const { createSqliteStore } = require('./server/stores/ops-sqlite.js');
- return createSqliteStore({ file });
- }
- function get() {
- if (impl) return impl;
- try {
- impl = build();
- lastFailure = null;
- return impl;
- } catch (e) {
- lastFailure = e;
- throw e;
- }
- }
- function reset() {
- impl = null;
- initPromise = null;
- }
- async function init() {
- if (!initPromise) {
- initPromise = Promise.resolve()
- .then(() => get())
- .then((store) => store.init())
- .then(() => get())
- .catch((e) => {
- lastFailure = e;
- initPromise = null;
- throw e;
- });
- }
- return initPromise;
- }
- /** never throws:{ ok, kind, reason?, detail? } */
- async function health() {
- try {
- await init();
- const result = await get().health();
- return { ...result, kind: driver };
- } catch (e) {
- return {
- ok: false,
- kind: driver,
- reason: e.reason || (e.code === 'MODULE_NOT_FOUND' ? 'driver-missing' : 'storage-unreachable'),
- detail: e.message || String(e),
- stack: undefined,
- };
- }
- }
- /** 包装:统一先 ensure init,再调用驱动方法 */
- function call(name) {
- return async (...args) => {
- await init();
- return get()[name](...args);
- };
- }
- return {
- kind: driver,
- init,
- health,
- reset,
- close: async () => {
- if (impl && typeof impl.close === 'function') {
- try { await impl.close(); } catch { /* ignore */ }
- }
- reset();
- },
- withTransaction: async (fn) => {
- await init();
- return get().withTransaction(fn);
- },
- findUploadBySha: call('findUploadBySha'),
- listUploads: call('listUploads'),
- getUpload: call('getUpload'),
- getContent: call('getContent'),
- queryRows: call('queryRows'),
- queryPlatformRows: call('queryPlatformRows'),
- summary: call('summary'),
- get failure() {
- return lastFailure;
- },
- };
- }
- module.exports = { createOpsStore, pickDriver };
|