@objectstack/rest 9.9.1 → 9.11.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.
package/dist/index.d.cts CHANGED
@@ -237,7 +237,8 @@ declare class RestServer {
237
237
  private sharingRulesServiceProvider?;
238
238
  private i18nServiceProvider?;
239
239
  private analyticsServiceProvider?;
240
- constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
240
+ private settingsServiceProvider?;
241
+ constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, settingsServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
241
242
  /**
242
243
  * Resolve the protocol for a given request. When `environmentId` is present
243
244
  * and a KernelManager is wired, fetch the per-project kernel's
package/dist/index.d.ts CHANGED
@@ -237,7 +237,8 @@ declare class RestServer {
237
237
  private sharingRulesServiceProvider?;
238
238
  private i18nServiceProvider?;
239
239
  private analyticsServiceProvider?;
240
- constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
240
+ private settingsServiceProvider?;
241
+ constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, settingsServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
241
242
  /**
242
243
  * Resolve the protocol for a given request. When `environmentId` is present
243
244
  * and a KernelManager is wired, fetch the per-project kernel's
package/dist/index.js CHANGED
@@ -449,7 +449,7 @@ function rowsToCsv(fields, rows, includeHeader) {
449
449
  return lines.join("\r\n") + (lines.length > 0 ? "\r\n" : "");
450
450
  }
451
451
  var RestServer = class {
452
- constructor(server, protocol, config = {}, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider, analyticsServiceProvider) {
452
+ constructor(server, protocol, config = {}, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider, analyticsServiceProvider, settingsServiceProvider) {
453
453
  /**
454
454
  * Short-TTL cache for `hostname → environmentId` (P1-4). `resolveByHostname`
455
455
  * is a control-plane lookup (typically a DB query) that otherwise runs on
@@ -480,6 +480,7 @@ var RestServer = class {
480
480
  this.sharingRulesServiceProvider = sharingRulesServiceProvider;
481
481
  this.i18nServiceProvider = i18nServiceProvider;
482
482
  this.analyticsServiceProvider = analyticsServiceProvider;
483
+ this.settingsServiceProvider = settingsServiceProvider;
483
484
  }
484
485
  /**
485
486
  * Resolve the protocol for a given request. When `environmentId` is present
@@ -769,6 +770,7 @@ var RestServer = class {
769
770
  }
770
771
  let userId;
771
772
  let tenantId;
773
+ let email;
772
774
  const keyPrincipal = await resolveApiKeyPrincipal(identityQl, headers).catch(() => void 0);
773
775
  if (keyPrincipal) {
774
776
  userId = keyPrincipal.userId;
@@ -779,6 +781,11 @@ var RestServer = class {
779
781
  if (!session?.user?.id) return void 0;
780
782
  userId = session.user.id;
781
783
  tenantId = session.session?.activeOrganizationId ?? void 0;
784
+ if (session.user?.email) email = String(session.user.email);
785
+ }
786
+ if (!email && identityQl && typeof identityQl.find === "function") {
787
+ const urows = await identityQl.find("sys_user", { where: { id: userId }, limit: 1, context: { isSystem: true } }).catch(() => []);
788
+ if (urows?.[0]?.email) email = String(urows[0].email);
782
789
  }
783
790
  try {
784
791
  let ql;
@@ -869,14 +876,34 @@ var RestServer = class {
869
876
  } catch {
870
877
  }
871
878
  }
879
+ let timezone;
880
+ let locale;
881
+ try {
882
+ const settings = this.settingsServiceProvider ? await this.settingsServiceProvider(environmentId).catch(() => void 0) : void 0;
883
+ if (settings && typeof settings.get === "function") {
884
+ const sctx = { tenantId, userId };
885
+ const [tzRes, localeRes] = await Promise.all([
886
+ settings.get("localization", "timezone", sctx).catch(() => void 0),
887
+ settings.get("localization", "locale", sctx).catch(() => void 0)
888
+ ]);
889
+ const tzVal = tzRes?.value;
890
+ const localeVal = localeRes?.value;
891
+ if (typeof tzVal === "string" && tzVal.trim()) timezone = tzVal.trim();
892
+ if (typeof localeVal === "string" && localeVal.trim()) locale = localeVal.trim();
893
+ }
894
+ } catch {
895
+ }
872
896
  return {
873
897
  userId,
874
898
  tenantId,
899
+ email,
875
900
  roles,
876
901
  permissions,
877
902
  systemPermissions,
878
903
  isSystem: false,
879
- org_user_ids
904
+ org_user_ids,
905
+ ...timezone ? { timezone } : {},
906
+ ...locale ? { locale } : {}
880
907
  };
881
908
  } catch {
882
909
  return void 0;
@@ -2943,6 +2970,7 @@ var RestServer = class {
2943
2970
  Object.assign(filteredData, rawBody);
2944
2971
  }
2945
2972
  const context = {
2973
+ publicFormGrant: { object: match.object },
2946
2974
  permissions: ["guest_portal"],
2947
2975
  anonymous: true
2948
2976
  };
@@ -4409,6 +4437,13 @@ function createRestApiPlugin(config = {}) {
4409
4437
  return void 0;
4410
4438
  }
4411
4439
  };
4440
+ const settingsServiceProvider = async (_environmentId) => {
4441
+ try {
4442
+ return ctx.getService("settings");
4443
+ } catch {
4444
+ return void 0;
4445
+ }
4446
+ };
4412
4447
  if (!server) {
4413
4448
  ctx.logger.warn(`RestApiPlugin: HTTP Server service '${serverService}' not found. REST routes skipped.`);
4414
4449
  return;
@@ -4419,9 +4454,14 @@ function createRestApiPlugin(config = {}) {
4419
4454
  }
4420
4455
  ctx.logger.info("Hydrating REST API from Protocol...");
4421
4456
  try {
4422
- const restServer = new RestServer(server, protocol, config.api, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider, analyticsServiceProvider);
4457
+ const restServer = new RestServer(server, protocol, config.api, kernelManager, envRegistry, defaultEnvironmentIdProvider, authServiceProvider, objectQLProvider, emailServiceProvider, sharingServiceProvider, reportsServiceProvider, approvalsServiceProvider, sharingRulesServiceProvider, i18nServiceProvider, analyticsServiceProvider, settingsServiceProvider);
4423
4458
  restServer.registerRoutes();
4424
4459
  ctx.logger.info("REST API successfully registered");
4460
+ if (!config.api?.requireAuth) {
4461
+ ctx.logger.warn(
4462
+ "[security] anonymous access to the data API is ALLOWED (api.requireAuth=false) \u2014 objects without OWD/RLS are world-readable. For secure-by-default set api.requireAuth=true and expose public records via share-links / publicSharing (ADR-0056 D2)."
4463
+ );
4464
+ }
4425
4465
  } catch (err) {
4426
4466
  ctx.logger.error("Failed to register REST API routes", { error: err.message });
4427
4467
  throw err;