@objectstack/plugin-auth 4.1.0 → 4.1.1

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.js CHANGED
@@ -779,6 +779,54 @@ var AuthManager = class {
779
779
  // who wire a real mailer can re-enable downstream.
780
780
  requireEmailVerificationOnInvitation: false,
781
781
  ...customOrgRoles ? { roles: customOrgRoles } : {},
782
+ // ── Slug-change guard ─────────────────────────────────────
783
+ // An org's slug is baked into every env hostname at creation
784
+ // time (see service-tenant `project-provisioning.ts`). Renaming
785
+ // it while live envs exist would silently desync the URL from
786
+ // the org identity. Block the change here; the cloud Console
787
+ // surfaces this as an actionable error and points users to
788
+ // `change_hostname` or archiving the env. Org `name` (display
789
+ // label) is unaffected — only `slug` is guarded.
790
+ //
791
+ // We resolve the data engine lazily so non-cloud apps (which
792
+ // never seed `sys_environment`) keep working: any lookup error
793
+ // is treated as "no envs to protect".
794
+ organizationHooks: {
795
+ beforeUpdateOrganization: async ({ organization: organization2, member }) => {
796
+ const newSlug = organization2?.slug;
797
+ const orgId = member?.organizationId;
798
+ if (!newSlug || !orgId) return;
799
+ const dataEngine2 = this.config.dataEngine;
800
+ if (!dataEngine2) return;
801
+ let currentSlug;
802
+ try {
803
+ const current = await dataEngine2.findOne("sys_organization", {
804
+ where: { id: orgId }
805
+ });
806
+ currentSlug = current?.slug;
807
+ } catch {
808
+ return;
809
+ }
810
+ if (!currentSlug || currentSlug === newSlug) return;
811
+ let activeEnvs = 0;
812
+ try {
813
+ const envs = await dataEngine2.find("sys_environment", {
814
+ where: { organization_id: orgId }
815
+ });
816
+ activeEnvs = (envs ?? []).filter(
817
+ (e) => e?.status !== "archived" && e?.status !== "failed"
818
+ ).length;
819
+ } catch {
820
+ return;
821
+ }
822
+ if (activeEnvs > 0) {
823
+ const { APIError } = await import("better-auth/api");
824
+ throw new APIError("FORBIDDEN", {
825
+ message: `Cannot change organization slug while ${activeEnvs} active environment(s) still reference it. Archive those environments or rename their hostnames first.`
826
+ });
827
+ }
828
+ }
829
+ },
782
830
  // No mailer is wired in framework yet — log the accept URL so
783
831
  // operators / UI can fall back to copy-paste flows. Replace this
784
832
  // with a real mail integration when available.
@@ -1224,6 +1272,30 @@ var AuthPlugin = class {
1224
1272
  if (!this.authManager) {
1225
1273
  throw new Error("Auth manager not initialized");
1226
1274
  }
1275
+ ctx.hook("kernel:ready", async () => {
1276
+ try {
1277
+ const i18n = ctx.getService("i18n");
1278
+ let loaded = 0;
1279
+ for (const [locale, data] of Object.entries(import_apps.SetupAppTranslations)) {
1280
+ if (data && typeof data === "object") {
1281
+ try {
1282
+ i18n.loadTranslations(locale, data);
1283
+ loaded++;
1284
+ } catch (err) {
1285
+ ctx.logger.warn(
1286
+ `Auth: failed to load Setup App translations for '${locale}': ${err?.message ?? err}`
1287
+ );
1288
+ }
1289
+ }
1290
+ }
1291
+ if (loaded > 0) {
1292
+ ctx.logger.info(
1293
+ `Auth: contributed Setup App translations (${loaded} locale${loaded > 1 ? "s" : ""})`
1294
+ );
1295
+ }
1296
+ } catch {
1297
+ }
1298
+ });
1227
1299
  if (this.options.registerRoutes) {
1228
1300
  ctx.hook("kernel:ready", async () => {
1229
1301
  if (this.authManager) {