@getstrata/bootstrap 0.2.62 → 0.2.64

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.
@@ -437,7 +437,6 @@ import {
437
437
  } from "@getstrata/core/queue/createAppQueue";
438
438
 
439
439
  // ../../src/bootstrap/queue/defaultJobs.ts
440
- import { DispatchWebhookJob } from "@getstrata/core/jobs/dispatchWebhookJob";
441
440
  import { ExportAuditLogsJob } from "@getstrata/core/jobs/exportAuditLogsJob";
442
441
  import { InvalidateCacheTagsJob as InvalidateCacheTagsJob2 } from "@getstrata/core/jobs/invalidateCacheTagsJob";
443
442
  import { jobRegistry } from "@getstrata/core/queue/jobRegistry";
@@ -446,7 +445,6 @@ function registerDefaultJobs() {
446
445
  jobRegistry.register("cache.invalidate-tags", () => {
447
446
  return new InvalidateCacheTagsJob2(resolveApplicationCache2());
448
447
  });
449
- jobRegistry.register("webhook.dispatch", () => new DispatchWebhookJob);
450
448
  jobRegistry.register("audit.export", () => new ExportAuditLogsJob);
451
449
  }
452
450
 
@@ -475,15 +473,160 @@ var storageProvider = {
475
473
  var storage_default = storageProvider;
476
474
 
477
475
  // ../../src/bootstrap/providers/view.ts
476
+ import { resolveMembershipLookup } from "@getstrata/core/auth/membershipContext";
478
477
  import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
478
+ import { appDisplayName } from "@getstrata/core/runtime/appKeyPrefix";
479
479
  import { isViewsEnabled } from "@getstrata/core/runtime/frontendMode";
480
480
  import {
481
481
  configureWebErrorView,
482
+ configureWebLayoutData,
482
483
  DEFAULT_VIEWS_DIRECTORY,
483
484
  EtaViewEngine,
484
485
  errorTemplateName,
485
486
  resolveWebLayoutData
486
487
  } from "@getstrata/core/view";
488
+
489
+ // ../../src/modules/organization/repository.ts
490
+ import { BaseRepository } from "@getstrata/core/database/baseRepository";
491
+ import { NotFoundError } from "@getstrata/core/errors/http";
492
+ import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
493
+
494
+ // ../../src/modules/organization/table.ts
495
+ import { defineTable } from "@getstrata/core/database/table";
496
+
497
+ // ../../src/domain/workhub.ts
498
+ var ORGANIZATION_TABLE = "organization";
499
+
500
+ // ../../src/modules/organization/table.ts
501
+ var organizationTable = defineTable({
502
+ name: ORGANIZATION_TABLE,
503
+ primaryKey: "id",
504
+ columns: ["id", "tenant_id", "name", "slug", "created_at", "updated_at", "deleted_at"],
505
+ softDeletes: true,
506
+ defaultOrderBy: { column: "id", direction: "ASC" }
507
+ });
508
+
509
+ // ../../src/modules/organization/repository.ts
510
+ class OrganizationRepository extends BaseRepository {
511
+ constructor() {
512
+ super(organizationTable);
513
+ }
514
+ async findBySlug(slug) {
515
+ return await this.firstOrNull({ slug });
516
+ }
517
+ async listForTenant(options) {
518
+ return await this.findAll({
519
+ limit: options.limit,
520
+ offset: options.offset,
521
+ where: { tenant_id: options.tenantId ?? currentTenantId() }
522
+ });
523
+ }
524
+ async countForTenant(tenantId = currentTenantId()) {
525
+ return await this.countWhere({ tenant_id: tenantId });
526
+ }
527
+ async findForTenantOrThrow(id, tenantId = currentTenantId()) {
528
+ const organization = await this.findById(id);
529
+ if (!organization || organization.tenant_id !== tenantId) {
530
+ throw new NotFoundError(`SCIM group ${id} not found.`);
531
+ }
532
+ return organization;
533
+ }
534
+ }
535
+ var repository_default = OrganizationRepository;
536
+
537
+ // ../../src/modules/user/repository.ts
538
+ import {
539
+ emailLookupForQuery,
540
+ protectEmail,
541
+ revealEmail
542
+ } from "@getstrata/core/crypto/fieldEncryption";
543
+ import { revealMfaSecret } from "@getstrata/core/crypto/mfaSecret";
544
+ import { BaseRepository as BaseRepository2 } from "@getstrata/core/database/baseRepository";
545
+ import { currentTenantId as currentTenantId2 } from "@getstrata/core/tenant/tenantContext";
546
+
547
+ // ../../src/modules/user/table.ts
548
+ import { defineTable as defineTable2 } from "@getstrata/core/database/table";
549
+ var userTable = defineTable2({
550
+ name: "users",
551
+ primaryKey: "id",
552
+ columns: [
553
+ "id",
554
+ "name",
555
+ "email",
556
+ "email_lookup",
557
+ "role",
558
+ "tenant_id",
559
+ "password_hash",
560
+ "email_verified_at",
561
+ "mfa_secret",
562
+ "mfa_enabled",
563
+ "mfa_recovery_codes",
564
+ "profile_photo_path",
565
+ "session_valid_after",
566
+ "current_organization_id",
567
+ "created_at",
568
+ "updated_at"
569
+ ],
570
+ defaultOrderBy: { column: "id", direction: "ASC" }
571
+ });
572
+
573
+ // ../../src/modules/user/repository.ts
574
+ class UserRepository extends BaseRepository2 {
575
+ constructor() {
576
+ super(userTable);
577
+ }
578
+ decode(record) {
579
+ return {
580
+ ...record,
581
+ email: revealEmail(record.email),
582
+ mfa_secret: revealMfaSecret(record.mfa_secret)
583
+ };
584
+ }
585
+ async findById(id) {
586
+ const record = await super.findById(id);
587
+ return record ? this.decode(record) : null;
588
+ }
589
+ async findAll(options = {}) {
590
+ const records = await super.findAll(options);
591
+ return records.map((record) => this.decode(record));
592
+ }
593
+ async create(values) {
594
+ const email = values.email;
595
+ if (!email) {
596
+ throw new Error("Email is required.");
597
+ }
598
+ const protectedEmail = protectEmail(email);
599
+ const record = await super.create({
600
+ ...values,
601
+ tenant_id: values.tenant_id ?? currentTenantId2(),
602
+ email: protectedEmail.storedEmail,
603
+ email_lookup: protectedEmail.emailLookup,
604
+ password_hash: values.password_hash ?? ""
605
+ });
606
+ return this.decode(record);
607
+ }
608
+ async updateByIdOrThrow(id, values, errorFactory) {
609
+ const changes = { ...values };
610
+ if (values.email !== undefined) {
611
+ const protectedEmail = protectEmail(values.email);
612
+ changes.email = protectedEmail.storedEmail;
613
+ changes.email_lookup = protectedEmail.emailLookup;
614
+ }
615
+ const record = await super.updateByIdOrThrow(id, changes, errorFactory);
616
+ return this.decode(record);
617
+ }
618
+ async findByEmail(email) {
619
+ const records = await this.findWhere({ email_lookup: emailLookupForQuery(email) }, { limit: 1 });
620
+ const record = records[0];
621
+ return record ? this.decode(record) : null;
622
+ }
623
+ async countForTenant(tenantId = currentTenantId2()) {
624
+ return await this.countWhere({ tenant_id: tenantId });
625
+ }
626
+ }
627
+ var repository_default2 = UserRepository;
628
+
629
+ // ../../src/bootstrap/providers/view.ts
487
630
  var CORE_VIEW_TOKEN = "core.view";
488
631
  var VIEW_DIRECTORY_CONFIG_KEY = "view.directory";
489
632
  var viewProvider = {
@@ -496,6 +639,25 @@ var viewProvider = {
496
639
  config.set(VIEW_DIRECTORY_CONFIG_KEY, viewsDirectory);
497
640
  const engine = new EtaViewEngine(viewsDirectory, (request) => resolveWebLayoutData(container, request ?? currentRequestMeta().request));
498
641
  container.set(CORE_VIEW_TOKEN, engine);
642
+ configureWebLayoutData({
643
+ extra: async (user) => {
644
+ const appName = appDisplayName();
645
+ if (!user || typeof user.id !== "number") {
646
+ return { appName, currentOrganization: null, organizations: [] };
647
+ }
648
+ try {
649
+ const record = await new repository_default2().findByIdOrThrow(user.id);
650
+ const memberships = await resolveMembershipLookup().listForUser(record.id);
651
+ const organizationsRepo = new repository_default;
652
+ const organizations = (await Promise.all(memberships.map((membership) => organizationsRepo.findById(membership.organization_id)))).filter((organization) => Boolean(organization && !organization.deleted_at));
653
+ const currentId = record.current_organization_id ?? null;
654
+ const currentOrganization = organizations.find((organization) => organization.id === currentId) ?? organizations[0] ?? null;
655
+ return { appName, currentOrganization, organizations };
656
+ } catch {
657
+ return { appName, currentOrganization: null, organizations: [] };
658
+ }
659
+ }
660
+ });
499
661
  configureWebErrorView({
500
662
  render: async (input) => engine.render(errorTemplateName(input.status), {
501
663
  title: input.title,
@@ -185,7 +185,8 @@ class HttpKernel {
185
185
  if (isEmailVerificationRequired() && !hasVerifiedEmail(user)) {
186
186
  return Response.redirect("/email/verify", 302);
187
187
  }
188
- return Response.redirect(home, 302);
188
+ const location = typeof home === "function" ? await home(user) : home;
189
+ return Response.redirect(location, 302);
189
190
  }
190
191
  return handler(request);
191
192
  });
@@ -2,15 +2,160 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/providers/view.ts
5
+ import { resolveMembershipLookup } from "@getstrata/core/auth/membershipContext";
5
6
  import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
7
+ import { appDisplayName } from "@getstrata/core/runtime/appKeyPrefix";
6
8
  import { isViewsEnabled } from "@getstrata/core/runtime/frontendMode";
7
9
  import {
8
10
  configureWebErrorView,
11
+ configureWebLayoutData,
9
12
  DEFAULT_VIEWS_DIRECTORY,
10
13
  EtaViewEngine,
11
14
  errorTemplateName,
12
15
  resolveWebLayoutData
13
16
  } from "@getstrata/core/view";
17
+
18
+ // ../../src/modules/organization/repository.ts
19
+ import { BaseRepository } from "@getstrata/core/database/baseRepository";
20
+ import { NotFoundError } from "@getstrata/core/errors/http";
21
+ import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
22
+
23
+ // ../../src/modules/organization/table.ts
24
+ import { defineTable } from "@getstrata/core/database/table";
25
+
26
+ // ../../src/domain/workhub.ts
27
+ var ORGANIZATION_TABLE = "organization";
28
+
29
+ // ../../src/modules/organization/table.ts
30
+ var organizationTable = defineTable({
31
+ name: ORGANIZATION_TABLE,
32
+ primaryKey: "id",
33
+ columns: ["id", "tenant_id", "name", "slug", "created_at", "updated_at", "deleted_at"],
34
+ softDeletes: true,
35
+ defaultOrderBy: { column: "id", direction: "ASC" }
36
+ });
37
+
38
+ // ../../src/modules/organization/repository.ts
39
+ class OrganizationRepository extends BaseRepository {
40
+ constructor() {
41
+ super(organizationTable);
42
+ }
43
+ async findBySlug(slug) {
44
+ return await this.firstOrNull({ slug });
45
+ }
46
+ async listForTenant(options) {
47
+ return await this.findAll({
48
+ limit: options.limit,
49
+ offset: options.offset,
50
+ where: { tenant_id: options.tenantId ?? currentTenantId() }
51
+ });
52
+ }
53
+ async countForTenant(tenantId = currentTenantId()) {
54
+ return await this.countWhere({ tenant_id: tenantId });
55
+ }
56
+ async findForTenantOrThrow(id, tenantId = currentTenantId()) {
57
+ const organization = await this.findById(id);
58
+ if (!organization || organization.tenant_id !== tenantId) {
59
+ throw new NotFoundError(`SCIM group ${id} not found.`);
60
+ }
61
+ return organization;
62
+ }
63
+ }
64
+ var repository_default = OrganizationRepository;
65
+
66
+ // ../../src/modules/user/repository.ts
67
+ import {
68
+ emailLookupForQuery,
69
+ protectEmail,
70
+ revealEmail
71
+ } from "@getstrata/core/crypto/fieldEncryption";
72
+ import { revealMfaSecret } from "@getstrata/core/crypto/mfaSecret";
73
+ import { BaseRepository as BaseRepository2 } from "@getstrata/core/database/baseRepository";
74
+ import { currentTenantId as currentTenantId2 } from "@getstrata/core/tenant/tenantContext";
75
+
76
+ // ../../src/modules/user/table.ts
77
+ import { defineTable as defineTable2 } from "@getstrata/core/database/table";
78
+ var userTable = defineTable2({
79
+ name: "users",
80
+ primaryKey: "id",
81
+ columns: [
82
+ "id",
83
+ "name",
84
+ "email",
85
+ "email_lookup",
86
+ "role",
87
+ "tenant_id",
88
+ "password_hash",
89
+ "email_verified_at",
90
+ "mfa_secret",
91
+ "mfa_enabled",
92
+ "mfa_recovery_codes",
93
+ "profile_photo_path",
94
+ "session_valid_after",
95
+ "current_organization_id",
96
+ "created_at",
97
+ "updated_at"
98
+ ],
99
+ defaultOrderBy: { column: "id", direction: "ASC" }
100
+ });
101
+
102
+ // ../../src/modules/user/repository.ts
103
+ class UserRepository extends BaseRepository2 {
104
+ constructor() {
105
+ super(userTable);
106
+ }
107
+ decode(record) {
108
+ return {
109
+ ...record,
110
+ email: revealEmail(record.email),
111
+ mfa_secret: revealMfaSecret(record.mfa_secret)
112
+ };
113
+ }
114
+ async findById(id) {
115
+ const record = await super.findById(id);
116
+ return record ? this.decode(record) : null;
117
+ }
118
+ async findAll(options = {}) {
119
+ const records = await super.findAll(options);
120
+ return records.map((record) => this.decode(record));
121
+ }
122
+ async create(values) {
123
+ const email = values.email;
124
+ if (!email) {
125
+ throw new Error("Email is required.");
126
+ }
127
+ const protectedEmail = protectEmail(email);
128
+ const record = await super.create({
129
+ ...values,
130
+ tenant_id: values.tenant_id ?? currentTenantId2(),
131
+ email: protectedEmail.storedEmail,
132
+ email_lookup: protectedEmail.emailLookup,
133
+ password_hash: values.password_hash ?? ""
134
+ });
135
+ return this.decode(record);
136
+ }
137
+ async updateByIdOrThrow(id, values, errorFactory) {
138
+ const changes = { ...values };
139
+ if (values.email !== undefined) {
140
+ const protectedEmail = protectEmail(values.email);
141
+ changes.email = protectedEmail.storedEmail;
142
+ changes.email_lookup = protectedEmail.emailLookup;
143
+ }
144
+ const record = await super.updateByIdOrThrow(id, changes, errorFactory);
145
+ return this.decode(record);
146
+ }
147
+ async findByEmail(email) {
148
+ const records = await this.findWhere({ email_lookup: emailLookupForQuery(email) }, { limit: 1 });
149
+ const record = records[0];
150
+ return record ? this.decode(record) : null;
151
+ }
152
+ async countForTenant(tenantId = currentTenantId2()) {
153
+ return await this.countWhere({ tenant_id: tenantId });
154
+ }
155
+ }
156
+ var repository_default2 = UserRepository;
157
+
158
+ // ../../src/bootstrap/providers/view.ts
14
159
  var CORE_VIEW_TOKEN = "core.view";
15
160
  var VIEW_DIRECTORY_CONFIG_KEY = "view.directory";
16
161
  var viewProvider = {
@@ -23,6 +168,25 @@ var viewProvider = {
23
168
  config.set(VIEW_DIRECTORY_CONFIG_KEY, viewsDirectory);
24
169
  const engine = new EtaViewEngine(viewsDirectory, (request) => resolveWebLayoutData(container, request ?? currentRequestMeta().request));
25
170
  container.set(CORE_VIEW_TOKEN, engine);
171
+ configureWebLayoutData({
172
+ extra: async (user) => {
173
+ const appName = appDisplayName();
174
+ if (!user || typeof user.id !== "number") {
175
+ return { appName, currentOrganization: null, organizations: [] };
176
+ }
177
+ try {
178
+ const record = await new repository_default2().findByIdOrThrow(user.id);
179
+ const memberships = await resolveMembershipLookup().listForUser(record.id);
180
+ const organizationsRepo = new repository_default;
181
+ const organizations = (await Promise.all(memberships.map((membership) => organizationsRepo.findById(membership.organization_id)))).filter((organization) => Boolean(organization && !organization.deleted_at));
182
+ const currentId = record.current_organization_id ?? null;
183
+ const currentOrganization = organizations.find((organization) => organization.id === currentId) ?? organizations[0] ?? null;
184
+ return { appName, currentOrganization, organizations };
185
+ } catch {
186
+ return { appName, currentOrganization: null, organizations: [] };
187
+ }
188
+ }
189
+ });
26
190
  configureWebErrorView({
27
191
  render: async (input) => engine.render(errorTemplateName(input.status), {
28
192
  title: input.title,
@@ -427,7 +427,6 @@ import {
427
427
  } from "@getstrata/core/queue/createAppQueue";
428
428
 
429
429
  // ../../src/bootstrap/queue/defaultJobs.ts
430
- import { DispatchWebhookJob } from "@getstrata/core/jobs/dispatchWebhookJob";
431
430
  import { ExportAuditLogsJob } from "@getstrata/core/jobs/exportAuditLogsJob";
432
431
  import { InvalidateCacheTagsJob as InvalidateCacheTagsJob2 } from "@getstrata/core/jobs/invalidateCacheTagsJob";
433
432
  import { jobRegistry } from "@getstrata/core/queue/jobRegistry";
@@ -436,7 +435,6 @@ function registerDefaultJobs() {
436
435
  jobRegistry.register("cache.invalidate-tags", () => {
437
436
  return new InvalidateCacheTagsJob2(resolveApplicationCache2());
438
437
  });
439
- jobRegistry.register("webhook.dispatch", () => new DispatchWebhookJob);
440
438
  jobRegistry.register("audit.export", () => new ExportAuditLogsJob);
441
439
  }
442
440
 
@@ -465,15 +463,160 @@ var storageProvider = {
465
463
  var storage_default = storageProvider;
466
464
 
467
465
  // ../../src/bootstrap/providers/view.ts
466
+ import { resolveMembershipLookup } from "@getstrata/core/auth/membershipContext";
468
467
  import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
468
+ import { appDisplayName } from "@getstrata/core/runtime/appKeyPrefix";
469
469
  import { isViewsEnabled } from "@getstrata/core/runtime/frontendMode";
470
470
  import {
471
471
  configureWebErrorView,
472
+ configureWebLayoutData,
472
473
  DEFAULT_VIEWS_DIRECTORY,
473
474
  EtaViewEngine,
474
475
  errorTemplateName,
475
476
  resolveWebLayoutData
476
477
  } from "@getstrata/core/view";
478
+
479
+ // ../../src/modules/organization/repository.ts
480
+ import { BaseRepository } from "@getstrata/core/database/baseRepository";
481
+ import { NotFoundError } from "@getstrata/core/errors/http";
482
+ import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
483
+
484
+ // ../../src/modules/organization/table.ts
485
+ import { defineTable } from "@getstrata/core/database/table";
486
+
487
+ // ../../src/domain/workhub.ts
488
+ var ORGANIZATION_TABLE = "organization";
489
+
490
+ // ../../src/modules/organization/table.ts
491
+ var organizationTable = defineTable({
492
+ name: ORGANIZATION_TABLE,
493
+ primaryKey: "id",
494
+ columns: ["id", "tenant_id", "name", "slug", "created_at", "updated_at", "deleted_at"],
495
+ softDeletes: true,
496
+ defaultOrderBy: { column: "id", direction: "ASC" }
497
+ });
498
+
499
+ // ../../src/modules/organization/repository.ts
500
+ class OrganizationRepository extends BaseRepository {
501
+ constructor() {
502
+ super(organizationTable);
503
+ }
504
+ async findBySlug(slug) {
505
+ return await this.firstOrNull({ slug });
506
+ }
507
+ async listForTenant(options) {
508
+ return await this.findAll({
509
+ limit: options.limit,
510
+ offset: options.offset,
511
+ where: { tenant_id: options.tenantId ?? currentTenantId() }
512
+ });
513
+ }
514
+ async countForTenant(tenantId = currentTenantId()) {
515
+ return await this.countWhere({ tenant_id: tenantId });
516
+ }
517
+ async findForTenantOrThrow(id, tenantId = currentTenantId()) {
518
+ const organization = await this.findById(id);
519
+ if (!organization || organization.tenant_id !== tenantId) {
520
+ throw new NotFoundError(`SCIM group ${id} not found.`);
521
+ }
522
+ return organization;
523
+ }
524
+ }
525
+ var repository_default = OrganizationRepository;
526
+
527
+ // ../../src/modules/user/repository.ts
528
+ import {
529
+ emailLookupForQuery,
530
+ protectEmail,
531
+ revealEmail
532
+ } from "@getstrata/core/crypto/fieldEncryption";
533
+ import { revealMfaSecret } from "@getstrata/core/crypto/mfaSecret";
534
+ import { BaseRepository as BaseRepository2 } from "@getstrata/core/database/baseRepository";
535
+ import { currentTenantId as currentTenantId2 } from "@getstrata/core/tenant/tenantContext";
536
+
537
+ // ../../src/modules/user/table.ts
538
+ import { defineTable as defineTable2 } from "@getstrata/core/database/table";
539
+ var userTable = defineTable2({
540
+ name: "users",
541
+ primaryKey: "id",
542
+ columns: [
543
+ "id",
544
+ "name",
545
+ "email",
546
+ "email_lookup",
547
+ "role",
548
+ "tenant_id",
549
+ "password_hash",
550
+ "email_verified_at",
551
+ "mfa_secret",
552
+ "mfa_enabled",
553
+ "mfa_recovery_codes",
554
+ "profile_photo_path",
555
+ "session_valid_after",
556
+ "current_organization_id",
557
+ "created_at",
558
+ "updated_at"
559
+ ],
560
+ defaultOrderBy: { column: "id", direction: "ASC" }
561
+ });
562
+
563
+ // ../../src/modules/user/repository.ts
564
+ class UserRepository extends BaseRepository2 {
565
+ constructor() {
566
+ super(userTable);
567
+ }
568
+ decode(record) {
569
+ return {
570
+ ...record,
571
+ email: revealEmail(record.email),
572
+ mfa_secret: revealMfaSecret(record.mfa_secret)
573
+ };
574
+ }
575
+ async findById(id) {
576
+ const record = await super.findById(id);
577
+ return record ? this.decode(record) : null;
578
+ }
579
+ async findAll(options = {}) {
580
+ const records = await super.findAll(options);
581
+ return records.map((record) => this.decode(record));
582
+ }
583
+ async create(values) {
584
+ const email = values.email;
585
+ if (!email) {
586
+ throw new Error("Email is required.");
587
+ }
588
+ const protectedEmail = protectEmail(email);
589
+ const record = await super.create({
590
+ ...values,
591
+ tenant_id: values.tenant_id ?? currentTenantId2(),
592
+ email: protectedEmail.storedEmail,
593
+ email_lookup: protectedEmail.emailLookup,
594
+ password_hash: values.password_hash ?? ""
595
+ });
596
+ return this.decode(record);
597
+ }
598
+ async updateByIdOrThrow(id, values, errorFactory) {
599
+ const changes = { ...values };
600
+ if (values.email !== undefined) {
601
+ const protectedEmail = protectEmail(values.email);
602
+ changes.email = protectedEmail.storedEmail;
603
+ changes.email_lookup = protectedEmail.emailLookup;
604
+ }
605
+ const record = await super.updateByIdOrThrow(id, changes, errorFactory);
606
+ return this.decode(record);
607
+ }
608
+ async findByEmail(email) {
609
+ const records = await this.findWhere({ email_lookup: emailLookupForQuery(email) }, { limit: 1 });
610
+ const record = records[0];
611
+ return record ? this.decode(record) : null;
612
+ }
613
+ async countForTenant(tenantId = currentTenantId2()) {
614
+ return await this.countWhere({ tenant_id: tenantId });
615
+ }
616
+ }
617
+ var repository_default2 = UserRepository;
618
+
619
+ // ../../src/bootstrap/providers/view.ts
477
620
  var CORE_VIEW_TOKEN = "core.view";
478
621
  var VIEW_DIRECTORY_CONFIG_KEY = "view.directory";
479
622
  var viewProvider = {
@@ -486,6 +629,25 @@ var viewProvider = {
486
629
  config.set(VIEW_DIRECTORY_CONFIG_KEY, viewsDirectory);
487
630
  const engine = new EtaViewEngine(viewsDirectory, (request) => resolveWebLayoutData(container, request ?? currentRequestMeta().request));
488
631
  container.set(CORE_VIEW_TOKEN, engine);
632
+ configureWebLayoutData({
633
+ extra: async (user) => {
634
+ const appName = appDisplayName();
635
+ if (!user || typeof user.id !== "number") {
636
+ return { appName, currentOrganization: null, organizations: [] };
637
+ }
638
+ try {
639
+ const record = await new repository_default2().findByIdOrThrow(user.id);
640
+ const memberships = await resolveMembershipLookup().listForUser(record.id);
641
+ const organizationsRepo = new repository_default;
642
+ const organizations = (await Promise.all(memberships.map((membership) => organizationsRepo.findById(membership.organization_id)))).filter((organization) => Boolean(organization && !organization.deleted_at));
643
+ const currentId = record.current_organization_id ?? null;
644
+ const currentOrganization = organizations.find((organization) => organization.id === currentId) ?? organizations[0] ?? null;
645
+ return { appName, currentOrganization, organizations };
646
+ } catch {
647
+ return { appName, currentOrganization: null, organizations: [] };
648
+ }
649
+ }
650
+ });
489
651
  configureWebErrorView({
490
652
  render: async (input) => engine.render(errorTemplateName(input.status), {
491
653
  title: input.title,
@@ -2,7 +2,6 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/queue/defaultJobs.ts
5
- import { DispatchWebhookJob } from "@getstrata/core/jobs/dispatchWebhookJob";
6
5
  import { ExportAuditLogsJob } from "@getstrata/core/jobs/exportAuditLogsJob";
7
6
  import { InvalidateCacheTagsJob } from "@getstrata/core/jobs/invalidateCacheTagsJob";
8
7
  import { jobRegistry } from "@getstrata/core/queue/jobRegistry";
@@ -11,7 +10,6 @@ function registerDefaultJobs() {
11
10
  jobRegistry.register("cache.invalidate-tags", () => {
12
11
  return new InvalidateCacheTagsJob(resolveApplicationCache());
13
12
  });
14
- jobRegistry.register("webhook.dispatch", () => new DispatchWebhookJob);
15
13
  jobRegistry.register("audit.export", () => new ExportAuditLogsJob);
16
14
  }
17
15
  export {
@@ -194,7 +194,8 @@ class HttpKernel {
194
194
  if (isEmailVerificationRequired() && !hasVerifiedEmail(user)) {
195
195
  return Response.redirect("/email/verify", 302);
196
196
  }
197
- return Response.redirect(home, 302);
197
+ const location = typeof home === "function" ? await home(user) : home;
198
+ return Response.redirect(location, 302);
198
199
  }
199
200
  return handler(request);
200
201
  });