@rpcbase/server 0.465.0 → 0.467.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ type ServerEnv = {
2
+ [key: string]: string | undefined;
3
+ };
4
+ export declare const checkInitReplicaSet: (serverEnv: ServerEnv) => Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=checkInitReplicaSet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkInitReplicaSet.d.ts","sourceRoot":"","sources":["../src/checkInitReplicaSet.ts"],"names":[],"mappings":"AAGA,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAMtD,eAAO,MAAM,mBAAmB,GAAU,WAAW,SAAS,KAAG,OAAO,CAAC,IAAI,CAiH5E,CAAA"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import session from "express-session";
2
2
  import { RedisStore } from "connect-redis";
3
3
  import MongoStore from "connect-mongo";
4
4
  import { createClient } from "redis";
5
+ import { MongoClient } from "mongodb";
5
6
  import env from "@rpcbase/env";
6
7
  import { initApiClient, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
7
8
  import { dirname, posix, sep } from "path";
@@ -3278,6 +3279,101 @@ const metricsIngestProxyMiddleware = (app) => {
3278
3279
  })
3279
3280
  );
3280
3281
  };
3282
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3283
+ let hasWarnedNoReplicationEnabled = false;
3284
+ const checkInitReplicaSet = async (serverEnv) => {
3285
+ const port = serverEnv.DB_PORT?.trim();
3286
+ if (!port) return;
3287
+ const host = (serverEnv.DB_HOST ?? "localhost").trim();
3288
+ const replSetName = "rs0";
3289
+ const memberHost = `${host}:${port}`;
3290
+ const maxAttempts = 10;
3291
+ const serverSelectionTimeoutMs = 2e3;
3292
+ const uri = `mongodb://${host}:${port}/?directConnection=true`;
3293
+ const waitForReplicaSetReady = async (admin) => {
3294
+ const maxWaitAttempts = 10;
3295
+ for (let attempt = 1; attempt <= maxWaitAttempts; attempt++) {
3296
+ try {
3297
+ const status = await admin.command({ replSetGetStatus: 1 });
3298
+ const state = Number(status?.myState ?? 0);
3299
+ if (status?.ok && (state === 1 || state === 2)) return;
3300
+ } catch {
3301
+ }
3302
+ await sleep(250 * attempt);
3303
+ }
3304
+ };
3305
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3306
+ let client2 = null;
3307
+ try {
3308
+ client2 = new MongoClient(uri, {
3309
+ family: 4,
3310
+ serverSelectionTimeoutMS: serverSelectionTimeoutMs,
3311
+ connectTimeoutMS: serverSelectionTimeoutMs
3312
+ });
3313
+ await client2.connect();
3314
+ const admin = client2.db("admin").admin();
3315
+ await admin.command({ ping: 1 });
3316
+ try {
3317
+ await admin.command({ replSetGetStatus: 1 });
3318
+ return;
3319
+ } catch (error) {
3320
+ const codeName = error?.codeName;
3321
+ if (codeName === "NotYetInitialized") {
3322
+ try {
3323
+ const res = await admin.command({
3324
+ replSetInitiate: {
3325
+ _id: replSetName,
3326
+ members: [{ _id: 0, host: memberHost }]
3327
+ }
3328
+ });
3329
+ if (res?.ok) {
3330
+ console.warn(`[rb/server] MongoDB replica set '${replSetName}' initiated (${memberHost}).`);
3331
+ } else {
3332
+ console.warn(`[rb/server] MongoDB replica set initiation returned ok=${String(res?.ok)}.`);
3333
+ }
3334
+ } catch (initError) {
3335
+ const initCodeName = initError?.codeName;
3336
+ if (initCodeName !== "AlreadyInitialized") {
3337
+ const message2 = initError instanceof Error ? initError.message : String(initError);
3338
+ console.warn(`[rb/server] MongoDB replica set initiation failed: ${message2}`);
3339
+ if (initCodeName === "InvalidReplicaSetConfig") {
3340
+ console.warn(
3341
+ `[rb/server] Hint: the replica set member host must match the mongod address/port. If MongoDB runs in Docker with port mapping, ensure the container listens on ${port} (e.g. mongod --port ${port}) instead of the default 27017.`
3342
+ );
3343
+ }
3344
+ }
3345
+ }
3346
+ await waitForReplicaSetReady(admin);
3347
+ return;
3348
+ }
3349
+ if (codeName === "NoReplicationEnabled") {
3350
+ if (!hasWarnedNoReplicationEnabled) {
3351
+ hasWarnedNoReplicationEnabled = true;
3352
+ console.warn(
3353
+ `[rb/server] MongoDB is not started with --replSet ${replSetName} (replication disabled). Change streams require a replica set; start mongod with --replSet.`
3354
+ );
3355
+ }
3356
+ return;
3357
+ }
3358
+ const message = error instanceof Error ? error.message : String(error);
3359
+ console.warn(`[rb/server] MongoDB replica set check failed: ${message}`);
3360
+ return;
3361
+ }
3362
+ } catch (error) {
3363
+ if (attempt === maxAttempts) {
3364
+ const message = error instanceof Error ? error.message : String(error);
3365
+ console.warn(`[rb/server] MongoDB replica set auto-init skipped (unable to connect to ${host}:${port}): ${message}`);
3366
+ return;
3367
+ }
3368
+ await sleep(250 * attempt);
3369
+ } finally {
3370
+ try {
3371
+ await client2?.close();
3372
+ } catch {
3373
+ }
3374
+ }
3375
+ }
3376
+ };
3281
3377
  process.env = {
3282
3378
  ...env,
3283
3379
  ...__rb_env__,
@@ -3297,17 +3393,24 @@ const getMongoUrl = (serverEnv) => {
3297
3393
  }
3298
3394
  return void 0;
3299
3395
  };
3300
- const createMongoSessionStore = (serverEnv) => {
3396
+ const createMongoSessionStore = async (serverEnv) => {
3301
3397
  const mongoUrl = getMongoUrl(serverEnv);
3302
3398
  if (!mongoUrl) {
3303
3399
  throw new Error("Missing REDIS_URL and Mongo connection details (MONGODB_URL/MONGO_URL/MONGODB_URI/DB_PORT)");
3304
3400
  }
3305
3401
  console.log("Using MongoDB session store");
3306
- return MongoStore.create({
3307
- mongoUrl,
3402
+ const client2 = await MongoClient.connect(mongoUrl, {
3403
+ family: 4,
3404
+ serverSelectionTimeoutMS: 2e3,
3405
+ connectTimeoutMS: 2e3
3406
+ });
3407
+ const store = MongoStore.create({
3408
+ client: client2,
3308
3409
  collectionName: "sessions",
3309
3410
  ttl: SESSION_MAX_AGE_S
3310
3411
  });
3412
+ await store.collectionP;
3413
+ return store;
3311
3414
  };
3312
3415
  const createRedisSessionStore = async (redisUrl) => {
3313
3416
  const reconnectStrategy = (retries) => {
@@ -3342,6 +3445,10 @@ const createRedisSessionStore = async (redisUrl) => {
3342
3445
  };
3343
3446
  const initServer = async (app, serverEnv) => {
3344
3447
  await initApiClient({ app });
3448
+ const replicaSetInitPromise = checkInitReplicaSet(serverEnv).catch((error) => {
3449
+ const message = error instanceof Error ? error.message : String(error);
3450
+ console.warn(`[rb/server] MongoDB replica set auto-init error: ${message}`);
3451
+ });
3345
3452
  app.disable("x-powered-by");
3346
3453
  app.set("trust proxy", true);
3347
3454
  app.use(requestIp.mw());
@@ -3364,7 +3471,13 @@ const initServer = async (app, serverEnv) => {
3364
3471
  }
3365
3472
  const sessionSecret = getDerivedKey(serverEnv.MASTER_KEY, "express_session_key");
3366
3473
  const redisUrl = serverEnv.REDIS_URL?.trim();
3367
- const store = redisUrl ? await createRedisSessionStore(redisUrl) : createMongoSessionStore(serverEnv);
3474
+ let store;
3475
+ if (redisUrl) {
3476
+ store = await createRedisSessionStore(redisUrl);
3477
+ } else {
3478
+ await replicaSetInitPromise;
3479
+ store = await createMongoSessionStore(serverEnv);
3480
+ }
3368
3481
  const sessionConfig = {
3369
3482
  name: "session",
3370
3483
  store,
@@ -3377,7 +3490,7 @@ const initServer = async (app, serverEnv) => {
3377
3490
  }
3378
3491
  };
3379
3492
  if (isProduction$1) {
3380
- sessionConfig.cookie.secure = true;
3493
+ sessionConfig.cookie.secure = "auto";
3381
3494
  }
3382
3495
  const sessionMiddleware = session(sessionConfig);
3383
3496
  app.locals.rbSessionMiddleware = sessionMiddleware;
@@ -1 +1 @@
1
- {"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAuBrC,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AA8EtD,eAAO,MAAM,UAAU,GAAU,KAAK,WAAW,EAAE,WAAW,SAAS,kBA6DtE,CAAA"}
1
+ {"version":3,"file":"initServer.d.ts","sourceRoot":"","sources":["../src/initServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAyBrC,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAuFtD,eAAO,MAAM,UAAU,GAAU,KAAK,WAAW,EAAE,WAAW,SAAS,kBAsEtE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/server",
3
- "version": "0.465.0",
3
+ "version": "0.467.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -75,6 +75,7 @@
75
75
  "connect-redis": "9.0.0",
76
76
  "express-session": "1.18.2",
77
77
  "http-proxy-middleware": "3.0.5",
78
+ "mongodb": "7.0.0",
78
79
  "redis": "5.10.0",
79
80
  "resend": "6.5.2",
80
81
  "ws": "8.18.0"