@logto/schemas 1.6.0 → 1.8.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.
Files changed (40) hide show
  1. package/LICENSE +0 -6
  2. package/alterations/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.ts +85 -0
  3. package/alterations/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts +88 -0
  4. package/alterations/1.7.0-1688627407-daily-active-users.ts +61 -0
  5. package/alterations/1.8.0-1692088012-add-is-suspend-column-to-tenants-table.ts +18 -0
  6. package/alterations/1.8.0-1692194751-add-affiliate-scopes.ts +57 -0
  7. package/alterations-js/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.d.ts +3 -0
  8. package/alterations-js/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.js +42 -0
  9. package/alterations-js/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.d.ts +3 -0
  10. package/alterations-js/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.js +55 -0
  11. package/alterations-js/1.7.0-1688627407-daily-active-users.d.ts +3 -0
  12. package/alterations-js/1.7.0-1688627407-daily-active-users.js +53 -0
  13. package/alterations-js/1.8.0-1692088012-add-is-suspend-column-to-tenants-table.d.ts +3 -0
  14. package/alterations-js/1.8.0-1692088012-add-is-suspend-column-to-tenants-table.js +14 -0
  15. package/alterations-js/1.8.0-1692194751-add-affiliate-scopes.d.ts +3 -0
  16. package/alterations-js/1.8.0-1692194751-add-affiliate-scopes.js +48 -0
  17. package/lib/consts/index.d.ts +1 -0
  18. package/lib/consts/index.js +1 -0
  19. package/lib/consts/tenant.d.ts +1 -0
  20. package/lib/consts/tenant.js +1 -0
  21. package/lib/db-entries/daily-active-user.d.ts +14 -0
  22. package/lib/db-entries/daily-active-user.js +32 -0
  23. package/lib/db-entries/index.d.ts +1 -0
  24. package/lib/db-entries/index.js +1 -0
  25. package/lib/foundations/jsonb-types.js +1 -1
  26. package/lib/models/tenants.d.ts +25 -4
  27. package/lib/models/tenants.js +5 -3
  28. package/lib/seeds/cloud-api.d.ts +5 -1
  29. package/lib/seeds/cloud-api.js +6 -0
  30. package/lib/seeds/logto-config.d.ts +6 -1
  31. package/lib/seeds/logto-config.js +10 -0
  32. package/lib/types/connector.d.ts +14 -5
  33. package/lib/types/connector.js +1 -0
  34. package/lib/types/log/index.d.ts +3 -1
  35. package/lib/types/logto-config.d.ts +16 -0
  36. package/lib/types/logto-config.js +8 -0
  37. package/lib/types/system.d.ts +11 -50
  38. package/lib/types/system.js +4 -21
  39. package/package.json +8 -8
  40. package/tables/daily_active_users.sql +13 -0
package/LICENSE CHANGED
@@ -1,9 +1,3 @@
1
- Portions of this software are licensed as follows:
2
-
3
- * All content that resides under the "packages/cloud" directory of this repository, if that directory exists, is licensed under the license defined in "packages/cloud/LICENSE" (Elastic-2.0).
4
- * All third party components incorporated into this software are licensed under the original license provided by the owner of the applicable component.
5
- * Content outside of the above mentioned directories or restrictions above is available under the "MPL-2.0" license as defined below.
6
-
7
1
  Mozilla Public License Version 2.0
8
2
  ==================================
9
3
 
@@ -0,0 +1,85 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const adminTenantId = 'admin';
6
+
7
+ const cloudServiceApplicationName = 'Cloud Service';
8
+
9
+ const cloudConnectionResourceIndicator = 'https://cloud.logto.io/api';
10
+
11
+ enum ApplicationType {
12
+ Native = 'Native',
13
+ SPA = 'SPA',
14
+ Traditional = 'Traditional',
15
+ MachineToMachine = 'MachineToMachine',
16
+ }
17
+
18
+ const cloudConnectionConfigKey = 'cloudConnection';
19
+
20
+ type Application = {
21
+ tenantId: string;
22
+ id: string;
23
+ name: string;
24
+ secret: string;
25
+ description: string;
26
+ type: ApplicationType;
27
+ oidcClientMetadata: unknown;
28
+ customClientMetadata: {
29
+ tenantId: string;
30
+ };
31
+ createdAt: number;
32
+ };
33
+
34
+ type CloudConnectionConfig = {
35
+ tenantId: string;
36
+ key: string;
37
+ value: unknown;
38
+ };
39
+
40
+ const alteration: AlterationScript = {
41
+ up: async (pool) => {
42
+ const { rows } = await pool.query<Application>(
43
+ sql`select * from applications where type = ${ApplicationType.MachineToMachine} and tenant_id = ${adminTenantId} and name = ${cloudServiceApplicationName}`
44
+ );
45
+
46
+ const { rows: existingCloudConnections } = await pool.query<CloudConnectionConfig>(sql`
47
+ select * from logto_configs where key = ${cloudConnectionConfigKey}
48
+ `);
49
+ const tenantIdsWithExistingRecords = new Set(
50
+ existingCloudConnections.map(({ tenantId }) => tenantId)
51
+ );
52
+ const filteredRows = rows.filter(
53
+ ({ customClientMetadata: { tenantId } }) => !tenantIdsWithExistingRecords.has(tenantId)
54
+ );
55
+
56
+ if (filteredRows.length === 0) {
57
+ return;
58
+ }
59
+
60
+ await pool.query(sql`
61
+ insert into logto_configs (tenant_id, key, value) values ${sql.join(
62
+ filteredRows.map(({ id, secret, customClientMetadata }) => {
63
+ const { tenantId } = customClientMetadata;
64
+ const cloudConnectionValue = {
65
+ appId: id,
66
+ appSecret: secret,
67
+ resource: cloudConnectionResourceIndicator,
68
+ };
69
+
70
+ return sql`(${tenantId}, ${cloudConnectionConfigKey}, ${JSON.stringify(
71
+ cloudConnectionValue
72
+ )})`;
73
+ }),
74
+ sql`,`
75
+ )}
76
+ `);
77
+ },
78
+ down: async (pool) => {
79
+ await pool.query(sql`
80
+ delete from logto_configs where key = ${cloudConnectionConfigKey}
81
+ `);
82
+ },
83
+ };
84
+
85
+ export default alteration;
@@ -0,0 +1,88 @@
1
+ import { GlobalValues } from '@logto/shared';
2
+ import { appendPath } from '@silverhand/essentials';
3
+ import { sql } from 'slonik';
4
+
5
+ import type { AlterationScript } from '../lib/types/alteration.js';
6
+
7
+ type M2mCredentials = {
8
+ appSecret: string;
9
+ appId: string;
10
+ endpoint: string;
11
+ tokenEndpoint: string;
12
+ resource: string;
13
+ };
14
+
15
+ type EmailServiceConnector = {
16
+ tenantId: string;
17
+ config: Partial<M2mCredentials> & Record<string, unknown>;
18
+ };
19
+ type CloudConnectionData = {
20
+ tenantId: string;
21
+ value: { appSecret: string; appId: string; resource: string };
22
+ };
23
+
24
+ enum ServiceConnector {
25
+ Email = 'logto-email',
26
+ }
27
+
28
+ const cloudConnectionKey = 'cloudConnection';
29
+
30
+ const alteration: AlterationScript = {
31
+ up: async (pool) => {
32
+ const { rows: rawConnectors } = await pool.query<EmailServiceConnector>(sql`
33
+ select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email};
34
+ `);
35
+ const connectors = rawConnectors.map((rawConnector) => {
36
+ const {
37
+ tenantId,
38
+ config: { appSecret, appId, endpoint, tokenEndpoint, resource, ...rest },
39
+ } = rawConnector;
40
+ return { tenantId, config: rest };
41
+ });
42
+ for (const connector of connectors) {
43
+ const { tenantId, config } = connector;
44
+ // eslint-disable-next-line no-await-in-loop
45
+ await pool.query(sql`
46
+ update connectors set config = ${JSON.stringify(
47
+ config
48
+ )} where tenant_id = ${tenantId} and connector_id = ${ServiceConnector.Email};
49
+ `);
50
+ }
51
+ },
52
+ down: async (pool) => {
53
+ const { rows: cloudConnections } = await pool.query<CloudConnectionData>(sql`
54
+ select tenant_id, value from logto_configs where key = ${cloudConnectionKey};
55
+ `);
56
+
57
+ /** Get `endpoint` and `tokenEndpoints` */
58
+ const globalValues = new GlobalValues();
59
+ const { cloudUrlSet, adminUrlSet } = globalValues;
60
+ const endpoint = appendPath(cloudUrlSet.endpoint, 'api').toString();
61
+ const tokenEndpoint = appendPath(adminUrlSet.endpoint, 'oidc/token').toString();
62
+
63
+ const { rows: rawEmailServiceConnectors } = await pool.query<EmailServiceConnector>(sql`
64
+ select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email};
65
+ `);
66
+ const tenantIdsWithM2mCredentials = new Set(cloudConnections.map(({ tenantId }) => tenantId));
67
+ const emailServiceConnectors = rawEmailServiceConnectors.filter(({ tenantId }) =>
68
+ tenantIdsWithM2mCredentials.has(tenantId)
69
+ );
70
+ for (const emailServiceConnector of emailServiceConnectors) {
71
+ const { tenantId: currentTenantId, config } = emailServiceConnector;
72
+ const newConfig = {
73
+ ...config,
74
+ endpoint,
75
+ tokenEndpoint,
76
+ ...cloudConnections.find(({ tenantId }) => tenantId === currentTenantId)?.value,
77
+ };
78
+ // eslint-disable-next-line no-await-in-loop
79
+ await pool.query(sql`
80
+ update connectors set config = ${JSON.stringify(
81
+ newConfig
82
+ )} where tenant_id = ${currentTenantId} and connector_id = ${ServiceConnector.Email};
83
+ `);
84
+ }
85
+ },
86
+ };
87
+
88
+ export default alteration;
@@ -0,0 +1,61 @@
1
+ import { type CommonQueryMethods, sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const getId = (value: string) => sql.identifier([value]);
6
+
7
+ const getDatabaseName = async (pool: CommonQueryMethods) => {
8
+ const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
9
+ select current_database();
10
+ `);
11
+
12
+ return currentDatabase.replaceAll('-', '_');
13
+ };
14
+
15
+ const alteration: AlterationScript = {
16
+ up: async (pool) => {
17
+ const database = await getDatabaseName(pool);
18
+ const baseRoleId = getId(`logto_tenant_${database}`);
19
+
20
+ await pool.query(sql`
21
+ create table daily_active_users (
22
+ id varchar(21) not null,
23
+ tenant_id varchar(21) not null
24
+ references tenants (id) on update cascade on delete cascade,
25
+ user_id varchar(21) not null,
26
+ date timestamptz not null,
27
+ primary key (id),
28
+ constraint daily_active_users__user_id_date
29
+ unique (user_id, date)
30
+ );
31
+
32
+ create index daily_active_users__id
33
+ on daily_active_users (tenant_id, id);
34
+
35
+ create trigger set_tenant_id before insert on daily_active_users
36
+ for each row execute procedure set_tenant_id();
37
+
38
+ alter table daily_active_users enable row level security;
39
+
40
+ create policy daily_active_users_tenant_id on daily_active_users
41
+ as restrictive
42
+ using (tenant_id = (select id from tenants where db_user = current_user));
43
+ create policy daily_active_users_modification on daily_active_users
44
+ using (true);
45
+
46
+ grant select, insert, update, delete on daily_active_users to ${baseRoleId};
47
+ `);
48
+ },
49
+ down: async (pool) => {
50
+ await pool.query(sql`
51
+ drop policy daily_active_users_tenant_id on daily_active_users;
52
+ drop policy daily_active_users_modification on daily_active_users;
53
+
54
+ alter table daily_active_users disable row level security;
55
+
56
+ drop table daily_active_users;
57
+ `);
58
+ },
59
+ };
60
+
61
+ export default alteration;
@@ -0,0 +1,18 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table tenants add column is_suspended boolean not null default false;
9
+ `);
10
+ },
11
+ down: async (pool) => {
12
+ await pool.query(sql`
13
+ alter table tenants drop column is_suspended;
14
+ `);
15
+ },
16
+ };
17
+
18
+ export default alteration;
@@ -0,0 +1,57 @@
1
+ import { generateStandardId } from '@logto/shared/universal';
2
+ import { sql } from 'slonik';
3
+
4
+ import type { AlterationScript } from '../lib/types/alteration.js';
5
+
6
+ const adminTenantId = 'admin';
7
+
8
+ const alteration: AlterationScript = {
9
+ up: async (pool) => {
10
+ // Get `resourceId` of the admin tenant's resource whose indicator is `https://cloud.logto.io/api`.
11
+ const { id: resourceId } = await pool.one<{ id: string }>(sql`
12
+ select id from resources
13
+ where tenant_id = ${adminTenantId}
14
+ and indicator = 'https://cloud.logto.io/api'
15
+ `);
16
+
17
+ const { id: roleId } = await pool.one<{ id: string }>(sql`
18
+ select id from roles
19
+ where tenant_id = ${adminTenantId}
20
+ and name = 'admin:admin'
21
+ `);
22
+
23
+ const createAffiliateId = generateStandardId();
24
+ const manageAffiliateId = generateStandardId();
25
+
26
+ await pool.query(sql`
27
+ insert into scopes (tenant_id, id, name, description, resource_id)
28
+ values (
29
+ ${adminTenantId},
30
+ ${createAffiliateId},
31
+ 'create:affiliate',
32
+ 'Allow creating new affiliates and logs.',
33
+ ${resourceId}
34
+ ), (
35
+ ${adminTenantId},
36
+ ${manageAffiliateId},
37
+ 'manage:affiliate',
38
+ 'Allow managing affiliates, including create, update, and delete.',
39
+ ${resourceId}
40
+ );
41
+ `);
42
+
43
+ await pool.query(sql`
44
+ insert into roles_scopes (tenant_id, id, role_id, scope_id) values
45
+ (${adminTenantId}, ${generateStandardId()}, ${roleId}, ${createAffiliateId}),
46
+ (${adminTenantId}, ${generateStandardId()}, ${roleId}, ${manageAffiliateId});
47
+ `);
48
+ },
49
+ down: async (pool) => {
50
+ await pool.query(sql`
51
+ delete from scopes
52
+ where tenant_id = ${adminTenantId} and name = any(array['create:affiliate', 'manage:affiliate']);
53
+ `);
54
+ },
55
+ };
56
+
57
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,42 @@
1
+ import { sql } from 'slonik';
2
+ const adminTenantId = 'admin';
3
+ const cloudServiceApplicationName = 'Cloud Service';
4
+ const cloudConnectionResourceIndicator = 'https://cloud.logto.io/api';
5
+ var ApplicationType;
6
+ (function (ApplicationType) {
7
+ ApplicationType["Native"] = "Native";
8
+ ApplicationType["SPA"] = "SPA";
9
+ ApplicationType["Traditional"] = "Traditional";
10
+ ApplicationType["MachineToMachine"] = "MachineToMachine";
11
+ })(ApplicationType || (ApplicationType = {}));
12
+ const cloudConnectionConfigKey = 'cloudConnection';
13
+ const alteration = {
14
+ up: async (pool) => {
15
+ const { rows } = await pool.query(sql `select * from applications where type = ${ApplicationType.MachineToMachine} and tenant_id = ${adminTenantId} and name = ${cloudServiceApplicationName}`);
16
+ const { rows: existingCloudConnections } = await pool.query(sql `
17
+ select * from logto_configs where key = ${cloudConnectionConfigKey}
18
+ `);
19
+ const tenantIdsWithExistingRecords = new Set(existingCloudConnections.map(({ tenantId }) => tenantId));
20
+ const filteredRows = rows.filter(({ customClientMetadata: { tenantId } }) => !tenantIdsWithExistingRecords.has(tenantId));
21
+ if (filteredRows.length === 0) {
22
+ return;
23
+ }
24
+ await pool.query(sql `
25
+ insert into logto_configs (tenant_id, key, value) values ${sql.join(filteredRows.map(({ id, secret, customClientMetadata }) => {
26
+ const { tenantId } = customClientMetadata;
27
+ const cloudConnectionValue = {
28
+ appId: id,
29
+ appSecret: secret,
30
+ resource: cloudConnectionResourceIndicator,
31
+ };
32
+ return sql `(${tenantId}, ${cloudConnectionConfigKey}, ${JSON.stringify(cloudConnectionValue)})`;
33
+ }), sql `,`)}
34
+ `);
35
+ },
36
+ down: async (pool) => {
37
+ await pool.query(sql `
38
+ delete from logto_configs where key = ${cloudConnectionConfigKey}
39
+ `);
40
+ },
41
+ };
42
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,55 @@
1
+ import { GlobalValues } from '@logto/shared';
2
+ import { appendPath } from '@silverhand/essentials';
3
+ import { sql } from 'slonik';
4
+ var ServiceConnector;
5
+ (function (ServiceConnector) {
6
+ ServiceConnector["Email"] = "logto-email";
7
+ })(ServiceConnector || (ServiceConnector = {}));
8
+ const cloudConnectionKey = 'cloudConnection';
9
+ const alteration = {
10
+ up: async (pool) => {
11
+ const { rows: rawConnectors } = await pool.query(sql `
12
+ select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email};
13
+ `);
14
+ const connectors = rawConnectors.map((rawConnector) => {
15
+ const { tenantId, config: { appSecret, appId, endpoint, tokenEndpoint, resource, ...rest }, } = rawConnector;
16
+ return { tenantId, config: rest };
17
+ });
18
+ for (const connector of connectors) {
19
+ const { tenantId, config } = connector;
20
+ // eslint-disable-next-line no-await-in-loop
21
+ await pool.query(sql `
22
+ update connectors set config = ${JSON.stringify(config)} where tenant_id = ${tenantId} and connector_id = ${ServiceConnector.Email};
23
+ `);
24
+ }
25
+ },
26
+ down: async (pool) => {
27
+ const { rows: cloudConnections } = await pool.query(sql `
28
+ select tenant_id, value from logto_configs where key = ${cloudConnectionKey};
29
+ `);
30
+ /** Get `endpoint` and `tokenEndpoints` */
31
+ const globalValues = new GlobalValues();
32
+ const { cloudUrlSet, adminUrlSet } = globalValues;
33
+ const endpoint = appendPath(cloudUrlSet.endpoint, 'api').toString();
34
+ const tokenEndpoint = appendPath(adminUrlSet.endpoint, 'oidc/token').toString();
35
+ const { rows: rawEmailServiceConnectors } = await pool.query(sql `
36
+ select tenant_id, config from connectors where connector_id = ${ServiceConnector.Email};
37
+ `);
38
+ const tenantIdsWithM2mCredentials = new Set(cloudConnections.map(({ tenantId }) => tenantId));
39
+ const emailServiceConnectors = rawEmailServiceConnectors.filter(({ tenantId }) => tenantIdsWithM2mCredentials.has(tenantId));
40
+ for (const emailServiceConnector of emailServiceConnectors) {
41
+ const { tenantId: currentTenantId, config } = emailServiceConnector;
42
+ const newConfig = {
43
+ ...config,
44
+ endpoint,
45
+ tokenEndpoint,
46
+ ...cloudConnections.find(({ tenantId }) => tenantId === currentTenantId)?.value,
47
+ };
48
+ // eslint-disable-next-line no-await-in-loop
49
+ await pool.query(sql `
50
+ update connectors set config = ${JSON.stringify(newConfig)} where tenant_id = ${currentTenantId} and connector_id = ${ServiceConnector.Email};
51
+ `);
52
+ }
53
+ },
54
+ };
55
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,53 @@
1
+ import { sql } from 'slonik';
2
+ const getId = (value) => sql.identifier([value]);
3
+ const getDatabaseName = async (pool) => {
4
+ const { currentDatabase } = await pool.one(sql `
5
+ select current_database();
6
+ `);
7
+ return currentDatabase.replaceAll('-', '_');
8
+ };
9
+ const alteration = {
10
+ up: async (pool) => {
11
+ const database = await getDatabaseName(pool);
12
+ const baseRoleId = getId(`logto_tenant_${database}`);
13
+ await pool.query(sql `
14
+ create table daily_active_users (
15
+ id varchar(21) not null,
16
+ tenant_id varchar(21) not null
17
+ references tenants (id) on update cascade on delete cascade,
18
+ user_id varchar(21) not null,
19
+ date timestamptz not null,
20
+ primary key (id),
21
+ constraint daily_active_users__user_id_date
22
+ unique (user_id, date)
23
+ );
24
+
25
+ create index daily_active_users__id
26
+ on daily_active_users (tenant_id, id);
27
+
28
+ create trigger set_tenant_id before insert on daily_active_users
29
+ for each row execute procedure set_tenant_id();
30
+
31
+ alter table daily_active_users enable row level security;
32
+
33
+ create policy daily_active_users_tenant_id on daily_active_users
34
+ as restrictive
35
+ using (tenant_id = (select id from tenants where db_user = current_user));
36
+ create policy daily_active_users_modification on daily_active_users
37
+ using (true);
38
+
39
+ grant select, insert, update, delete on daily_active_users to ${baseRoleId};
40
+ `);
41
+ },
42
+ down: async (pool) => {
43
+ await pool.query(sql `
44
+ drop policy daily_active_users_tenant_id on daily_active_users;
45
+ drop policy daily_active_users_modification on daily_active_users;
46
+
47
+ alter table daily_active_users disable row level security;
48
+
49
+ drop table daily_active_users;
50
+ `);
51
+ },
52
+ };
53
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,14 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table tenants add column is_suspended boolean not null default false;
6
+ `);
7
+ },
8
+ down: async (pool) => {
9
+ await pool.query(sql `
10
+ alter table tenants drop column is_suspended;
11
+ `);
12
+ },
13
+ };
14
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,48 @@
1
+ import { generateStandardId } from '@logto/shared/universal';
2
+ import { sql } from 'slonik';
3
+ const adminTenantId = 'admin';
4
+ const alteration = {
5
+ up: async (pool) => {
6
+ // Get `resourceId` of the admin tenant's resource whose indicator is `https://cloud.logto.io/api`.
7
+ const { id: resourceId } = await pool.one(sql `
8
+ select id from resources
9
+ where tenant_id = ${adminTenantId}
10
+ and indicator = 'https://cloud.logto.io/api'
11
+ `);
12
+ const { id: roleId } = await pool.one(sql `
13
+ select id from roles
14
+ where tenant_id = ${adminTenantId}
15
+ and name = 'admin:admin'
16
+ `);
17
+ const createAffiliateId = generateStandardId();
18
+ const manageAffiliateId = generateStandardId();
19
+ await pool.query(sql `
20
+ insert into scopes (tenant_id, id, name, description, resource_id)
21
+ values (
22
+ ${adminTenantId},
23
+ ${createAffiliateId},
24
+ 'create:affiliate',
25
+ 'Allow creating new affiliates and logs.',
26
+ ${resourceId}
27
+ ), (
28
+ ${adminTenantId},
29
+ ${manageAffiliateId},
30
+ 'manage:affiliate',
31
+ 'Allow managing affiliates, including create, update, and delete.',
32
+ ${resourceId}
33
+ );
34
+ `);
35
+ await pool.query(sql `
36
+ insert into roles_scopes (tenant_id, id, role_id, scope_id) values
37
+ (${adminTenantId}, ${generateStandardId()}, ${roleId}, ${createAffiliateId}),
38
+ (${adminTenantId}, ${generateStandardId()}, ${roleId}, ${manageAffiliateId});
39
+ `);
40
+ },
41
+ down: async (pool) => {
42
+ await pool.query(sql `
43
+ delete from scopes
44
+ where tenant_id = ${adminTenantId} and name = any(array['create:affiliate', 'manage:affiliate']);
45
+ `);
46
+ },
47
+ };
48
+ export default alteration;
@@ -2,3 +2,4 @@ export * from './cookie.js';
2
2
  export * from './system.js';
3
3
  export * from './oidc.js';
4
4
  export * from './date.js';
5
+ export * from './tenant.js';
@@ -2,3 +2,4 @@ export * from './cookie.js';
2
2
  export * from './system.js';
3
3
  export * from './oidc.js';
4
4
  export * from './date.js';
5
+ export * from './tenant.js';
@@ -0,0 +1 @@
1
+ export declare const maxFreeTenantLimit = 10;
@@ -0,0 +1 @@
1
+ export const maxFreeTenantLimit = 10;
@@ -0,0 +1,14 @@
1
+ import { GeneratedSchema } from './../foundations/index.js';
2
+ export type CreateDailyActiveUser = {
3
+ id: string;
4
+ tenantId?: string;
5
+ userId: string;
6
+ date: number;
7
+ };
8
+ export type DailyActiveUser = {
9
+ id: string;
10
+ tenantId: string;
11
+ userId: string;
12
+ date: number;
13
+ };
14
+ export declare const DailyActiveUsers: GeneratedSchema<CreateDailyActiveUser, DailyActiveUser>;
@@ -0,0 +1,32 @@
1
+ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ import { z } from 'zod';
3
+ const createGuard = z.object({
4
+ id: z.string().min(1).max(21),
5
+ tenantId: z.string().max(21).optional(),
6
+ userId: z.string().min(1).max(21),
7
+ date: z.number(),
8
+ });
9
+ const guard = z.object({
10
+ id: z.string().min(1).max(21),
11
+ tenantId: z.string().max(21),
12
+ userId: z.string().min(1).max(21),
13
+ date: z.number(),
14
+ });
15
+ export const DailyActiveUsers = Object.freeze({
16
+ table: 'daily_active_users',
17
+ tableSingular: 'daily_active_user',
18
+ fields: {
19
+ id: 'id',
20
+ tenantId: 'tenant_id',
21
+ userId: 'user_id',
22
+ date: 'date',
23
+ },
24
+ fieldKeys: [
25
+ 'id',
26
+ 'tenantId',
27
+ 'userId',
28
+ 'date',
29
+ ],
30
+ createGuard,
31
+ guard,
32
+ });
@@ -7,6 +7,7 @@ export * from './application.js';
7
7
  export * from './applications-role.js';
8
8
  export * from './connector.js';
9
9
  export * from './custom-phrase.js';
10
+ export * from './daily-active-user.js';
10
11
  export * from './domain.js';
11
12
  export * from './hook.js';
12
13
  export * from './log.js';
@@ -8,6 +8,7 @@ export * from './application.js';
8
8
  export * from './applications-role.js';
9
9
  export * from './connector.js';
10
10
  export * from './custom-phrase.js';
11
+ export * from './daily-active-user.js';
11
12
  export * from './domain.js';
12
13
  export * from './hook.js';
13
14
  export * from './log.js';
@@ -65,7 +65,7 @@ export var CustomClientMetadataKey;
65
65
  CustomClientMetadataKey["RotateRefreshToken"] = "rotateRefreshToken";
66
66
  })(CustomClientMetadataKey || (CustomClientMetadataKey = {}));
67
67
  export const customClientMetadataGuard = z.object({
68
- [CustomClientMetadataKey.CorsAllowedOrigins]: z.string().url().array().optional(),
68
+ [CustomClientMetadataKey.CorsAllowedOrigins]: z.string().min(1).array().optional(),
69
69
  [CustomClientMetadataKey.IdTokenTtl]: z.number().optional(),
70
70
  [CustomClientMetadataKey.RefreshTokenTtl]: z.number().optional(),
71
71
  [CustomClientMetadataKey.RefreshTokenTtlInDays]: z.number().int().min(1).max(90).optional(),
@@ -12,9 +12,30 @@ export declare const Tenants: import("@withtyped/server/model").default<"tenants
12
12
  name: string;
13
13
  tag: TenantTag;
14
14
  createdAt: Date;
15
- }, "name" | "createdAt" | "tag", "createdAt">;
15
+ isSuspended: boolean;
16
+ }, "name" | "createdAt" | "isSuspended" | "tag", "createdAt">;
16
17
  export type TenantModel = InferModelType<typeof Tenants>;
17
- export type TenantInfo = Pick<TenantModel, 'id' | 'name' | 'tag'> & {
18
+ export declare const tenantInfoGuard: z.ZodObject<z.extendShape<Pick<{
19
+ id: z.ZodType<string, z.ZodTypeDef, string>;
20
+ dbUser: z.ZodType<string | null, z.ZodTypeDef, string | null>;
21
+ dbUserPassword: z.ZodType<string | null, z.ZodTypeDef, string | null>;
22
+ name: z.ZodType<string, z.ZodTypeDef, string>;
23
+ tag: z.ZodType<TenantTag, z.ZodTypeDef, TenantTag>;
24
+ createdAt: z.ZodType<Date, z.ZodTypeDef, Date>;
25
+ isSuspended: z.ZodType<boolean, z.ZodTypeDef, boolean>;
26
+ }, "name" | "id" | "isSuspended" | "tag">, {
27
+ indicator: z.ZodString;
28
+ }>, "strip", z.ZodTypeAny, {
29
+ name: string;
30
+ id: string;
18
31
  indicator: string;
19
- };
20
- export declare const tenantInfoGuard: z.ZodType<TenantInfo>;
32
+ isSuspended: boolean;
33
+ tag: TenantTag;
34
+ }, {
35
+ name: string;
36
+ id: string;
37
+ indicator: string;
38
+ isSuspended: boolean;
39
+ tag: TenantTag;
40
+ }>;
41
+ export type TenantInfo = z.infer<typeof tenantInfoGuard>;
@@ -6,7 +6,8 @@ export var TenantTag;
6
6
  TenantTag["Staging"] = "staging";
7
7
  TenantTag["Production"] = "production";
8
8
  })(TenantTag || (TenantTag = {}));
9
- export const Tenants = createModel(/* sql */ `
9
+ export const Tenants = createModel(
10
+ /* Sql */ `
10
11
  /* init_order = 0 */
11
12
  create table tenants (
12
13
  id varchar(21) not null,
@@ -15,14 +16,15 @@ export const Tenants = createModel(/* sql */ `
15
16
  name varchar(128) not null default 'My Project',
16
17
  tag varchar(64) not null default '${TenantTag.Development}',
17
18
  created_at timestamptz not null default(now()),
19
+ is_suspended boolean not null default false,
18
20
  primary key (id),
19
21
  constraint tenants__db_user
20
22
  unique (db_user)
21
23
  );
22
24
  /* no_after_each */
23
- `)
25
+ `, 'public')
24
26
  .extend('tag', z.nativeEnum(TenantTag))
25
27
  .extend('createdAt', { readonly: true });
26
28
  export const tenantInfoGuard = Tenants.guard('model')
27
- .pick({ id: true, name: true, tag: true })
29
+ .pick({ id: true, name: true, tag: true, isSuspended: true })
28
30
  .extend({ indicator: z.string() });
@@ -10,7 +10,11 @@ export declare enum CloudScope {
10
10
  /** The user can update or delete its own tenants. */
11
11
  ManageTenantSelf = "manage:tenant:self",
12
12
  SendSms = "send:sms",
13
- SendEmail = "send:email"
13
+ SendEmail = "send:email",
14
+ /** The user can see and manage affiliates, including create, update, and delete. */
15
+ ManageAffiliate = "manage:affiliate",
16
+ /** The user can create new affiliates and logs. */
17
+ CreateAffiliate = "create:affiliate"
14
18
  }
15
19
  export declare const createCloudApi: () => readonly [UpdateAdminData, ...CreateScope[]];
16
20
  export declare const createTenantApplicationRole: () => Readonly<Role>;
@@ -13,6 +13,10 @@ export var CloudScope;
13
13
  CloudScope["ManageTenantSelf"] = "manage:tenant:self";
14
14
  CloudScope["SendSms"] = "send:sms";
15
15
  CloudScope["SendEmail"] = "send:email";
16
+ /** The user can see and manage affiliates, including create, update, and delete. */
17
+ CloudScope["ManageAffiliate"] = "manage:affiliate";
18
+ /** The user can create new affiliates and logs. */
19
+ CloudScope["CreateAffiliate"] = "create:affiliate";
16
20
  })(CloudScope || (CloudScope = {}));
17
21
  export const createCloudApi = () => {
18
22
  const resourceId = generateStandardId();
@@ -43,6 +47,8 @@ export const createCloudApi = () => {
43
47
  buildScope(CloudScope.ManageTenant, 'Allow managing existing tenants, including create without limitation, update, and delete.'),
44
48
  buildScope(CloudScope.SendEmail, 'Allow sending emails. This scope is only available to M2M application.'),
45
49
  buildScope(CloudScope.SendSms, 'Allow sending SMS. This scope is only available to M2M application.'),
50
+ buildScope(CloudScope.CreateAffiliate, 'Allow creating new affiliates and logs.'),
51
+ buildScope(CloudScope.ManageAffiliate, 'Allow managing affiliates, including create, update, and delete.'),
46
52
  ]);
47
53
  };
48
54
  export const createTenantApplicationRole = () => ({
@@ -1,7 +1,12 @@
1
- import type { AdminConsoleData } from '../types/index.js';
1
+ import type { AdminConsoleData, CloudConnectionData } from '../types/index.js';
2
2
  import { LogtoTenantConfigKey } from '../types/index.js';
3
3
  export declare const createDefaultAdminConsoleConfig: (forTenantId: string) => Readonly<{
4
4
  tenantId: string;
5
5
  key: LogtoTenantConfigKey;
6
6
  value: AdminConsoleData;
7
7
  }>;
8
+ export declare const createCloudConnectionConfig: (forTenantId: string, appId: string, appSecret: string) => Readonly<{
9
+ tenantId: string;
10
+ key: LogtoTenantConfigKey;
11
+ value: CloudConnectionData;
12
+ }>;
@@ -1,4 +1,5 @@
1
1
  import { LogtoTenantConfigKey } from '../types/index.js';
2
+ import { cloudApiIndicator } from './cloud-api.js';
2
3
  export const createDefaultAdminConsoleConfig = (forTenantId) => Object.freeze({
3
4
  tenantId: forTenantId,
4
5
  key: LogtoTenantConfigKey.AdminConsole,
@@ -13,3 +14,12 @@ export const createDefaultAdminConsoleConfig = (forTenantId) => Object.freeze({
13
14
  m2mApplicationCreated: false,
14
15
  },
15
16
  });
17
+ export const createCloudConnectionConfig = (forTenantId, appId, appSecret) => Object.freeze({
18
+ tenantId: forTenantId,
19
+ key: LogtoTenantConfigKey.CloudConnection,
20
+ value: {
21
+ appId,
22
+ appSecret,
23
+ resource: cloudApiIndicator,
24
+ },
25
+ });
@@ -9,6 +9,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
9
9
  connectorId: z.ZodType<string, z.ZodTypeDef, string>;
10
10
  config: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
11
11
  metadata: z.ZodType<{
12
+ [x: string]: unknown;
12
13
  target?: string | undefined;
13
14
  name?: ({
14
15
  en: string;
@@ -139,6 +140,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
139
140
  logo?: string | undefined;
140
141
  logoDark?: string | null | undefined;
141
142
  }, z.ZodTypeDef, {
143
+ [x: string]: unknown;
142
144
  target?: string | undefined;
143
145
  name?: ({
144
146
  en: string;
@@ -900,6 +902,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
900
902
  type: z.ZodNativeEnum<typeof ConnectorType>;
901
903
  isDemo: z.ZodOptional<z.ZodBoolean>;
902
904
  extraInfo: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
905
+ usage: z.ZodOptional<z.ZodNumber>;
903
906
  }>, "strip", z.ZodTypeAny, {
904
907
  isStandard?: boolean | undefined;
905
908
  configTemplate?: string | undefined;
@@ -938,6 +941,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
938
941
  })[] | undefined;
939
942
  isDemo?: boolean | undefined;
940
943
  extraInfo?: Record<string, unknown> | undefined;
944
+ usage?: number | undefined;
941
945
  type: ConnectorType;
942
946
  name: {
943
947
  en: string;
@@ -1196,6 +1200,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
1196
1200
  connectorId: string;
1197
1201
  config: import("@withtyped/server").JsonObject;
1198
1202
  metadata: {
1203
+ [x: string]: unknown;
1199
1204
  target?: string | undefined;
1200
1205
  name?: ({
1201
1206
  en: string;
@@ -1327,9 +1332,9 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
1327
1332
  logoDark?: string | null | undefined;
1328
1333
  };
1329
1334
  target: string;
1335
+ platform: import("@logto/connector-kit").ConnectorPlatform | null;
1330
1336
  logo: string;
1331
1337
  logoDark: string | null;
1332
- platform: import("@logto/connector-kit").ConnectorPlatform | null;
1333
1338
  readme: string;
1334
1339
  }, {
1335
1340
  isStandard?: boolean | undefined;
@@ -1369,6 +1374,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
1369
1374
  })[] | undefined;
1370
1375
  isDemo?: boolean | undefined;
1371
1376
  extraInfo?: Record<string, unknown> | undefined;
1377
+ usage?: number | undefined;
1372
1378
  type: ConnectorType;
1373
1379
  name: {
1374
1380
  en: string;
@@ -1627,6 +1633,7 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
1627
1633
  connectorId: string;
1628
1634
  config: import("@withtyped/server").JsonObject;
1629
1635
  metadata: {
1636
+ [x: string]: unknown;
1630
1637
  target?: string | undefined;
1631
1638
  name?: ({
1632
1639
  en: string;
@@ -1758,9 +1765,9 @@ export declare const connectorResponseGuard: z.ZodObject<z.extendShape<z.extendS
1758
1765
  logoDark?: string | null | undefined;
1759
1766
  };
1760
1767
  target: string;
1768
+ platform: import("@logto/connector-kit").ConnectorPlatform | null;
1761
1769
  logo: string;
1762
1770
  logoDark: string | null;
1763
- platform: import("@logto/connector-kit").ConnectorPlatform | null;
1764
1771
  readme: string;
1765
1772
  }>;
1766
1773
  export type ConnectorResponse = z.infer<typeof connectorResponseGuard>;
@@ -2393,7 +2400,8 @@ export declare const connectorFactoryResponseGuard: z.ZodObject<z.extendShape<{
2393
2400
  key: string;
2394
2401
  label: string;
2395
2402
  }>]>, "many">>;
2396
- }>, "strip", z.ZodTypeAny, {
2403
+ }>, "strip", z.ZodUnknown, {
2404
+ [x: string]: unknown;
2397
2405
  isStandard?: boolean | undefined;
2398
2406
  configTemplate?: string | undefined;
2399
2407
  formItems?: ({
@@ -2685,11 +2693,12 @@ export declare const connectorFactoryResponseGuard: z.ZodObject<z.extendShape<{
2685
2693
  "zz-TR"?: string | undefined;
2686
2694
  };
2687
2695
  target: string;
2696
+ platform: import("@logto/connector-kit").ConnectorPlatform | null;
2688
2697
  logo: string;
2689
2698
  logoDark: string | null;
2690
- platform: import("@logto/connector-kit").ConnectorPlatform | null;
2691
2699
  readme: string;
2692
2700
  }, {
2701
+ [x: string]: unknown;
2693
2702
  isStandard?: boolean | undefined;
2694
2703
  configTemplate?: string | undefined;
2695
2704
  formItems?: ({
@@ -2981,9 +2990,9 @@ export declare const connectorFactoryResponseGuard: z.ZodObject<z.extendShape<{
2981
2990
  "zz-TR"?: string | undefined;
2982
2991
  };
2983
2992
  target: string;
2993
+ platform: import("@logto/connector-kit").ConnectorPlatform | null;
2984
2994
  logo: string;
2985
2995
  logoDark: string | null;
2986
- platform: import("@logto/connector-kit").ConnectorPlatform | null;
2987
2996
  readme: string;
2988
2997
  }>;
2989
2998
  export type ConnectorFactoryResponse = z.infer<typeof connectorFactoryResponseGuard>;
@@ -15,6 +15,7 @@ export const connectorResponseGuard = Connectors.guard
15
15
  type: z.nativeEnum(ConnectorType),
16
16
  isDemo: z.boolean().optional(),
17
17
  extraInfo: z.record(z.unknown()).optional(),
18
+ usage: z.number().optional(),
18
19
  }));
19
20
  export const connectorFactoryResponseGuard = z
20
21
  .object({
@@ -6,6 +6,8 @@ export * as token from './token.js';
6
6
  export * as hook from './hook.js';
7
7
  /** Fallback for empty or unrecognized log keys. */
8
8
  export declare const LogKeyUnknown = "Unknown";
9
+ export type AuditLogKey = typeof LogKeyUnknown | interaction.LogKey | token.LogKey;
10
+ export type WebhookLogKey = hook.LogKey;
9
11
  /**
10
12
  * The union type of all available log keys.
11
13
  * Note duplicate keys are allowed but should be avoided.
@@ -13,4 +15,4 @@ export declare const LogKeyUnknown = "Unknown";
13
15
  * @see {@link interaction.LogKey} for interaction log keys.
14
16
  * @see {@link token.LogKey} for token log keys.
15
17
  **/
16
- export type LogKey = typeof LogKeyUnknown | interaction.LogKey | token.LogKey | hook.LogKey;
18
+ export type LogKey = AuditLogKey | WebhookLogKey;
@@ -40,13 +40,29 @@ export declare const adminConsoleDataGuard: z.ZodObject<{
40
40
  m2mApplicationCreated: boolean;
41
41
  }>;
42
42
  export type AdminConsoleData = z.infer<typeof adminConsoleDataGuard>;
43
+ export declare const cloudConnectionDataGuard: z.ZodObject<{
44
+ appId: z.ZodString;
45
+ appSecret: z.ZodString;
46
+ resource: z.ZodString;
47
+ }, "strip", z.ZodTypeAny, {
48
+ resource: string;
49
+ appId: string;
50
+ appSecret: string;
51
+ }, {
52
+ resource: string;
53
+ appId: string;
54
+ appSecret: string;
55
+ }>;
56
+ export type CloudConnectionData = z.infer<typeof cloudConnectionDataGuard>;
43
57
  export declare enum LogtoTenantConfigKey {
44
58
  AdminConsole = "adminConsole",
59
+ CloudConnection = "cloudConnection",
45
60
  /** The URL to redirect when session not found in Sign-in Experience. */
46
61
  SessionNotFoundRedirectUrl = "sessionNotFoundRedirectUrl"
47
62
  }
48
63
  export type LogtoTenantConfigType = {
49
64
  [LogtoTenantConfigKey.AdminConsole]: AdminConsoleData;
65
+ [LogtoTenantConfigKey.CloudConnection]: CloudConnectionData;
50
66
  [LogtoTenantConfigKey.SessionNotFoundRedirectUrl]: {
51
67
  url: string;
52
68
  };
@@ -21,14 +21,22 @@ export const adminConsoleDataGuard = z.object({
21
21
  roleCreated: z.boolean(),
22
22
  m2mApplicationCreated: z.boolean(),
23
23
  });
24
+ /* --- Logto tenant cloud connection config --- */
25
+ export const cloudConnectionDataGuard = z.object({
26
+ appId: z.string(),
27
+ appSecret: z.string(),
28
+ resource: z.string(),
29
+ });
24
30
  export var LogtoTenantConfigKey;
25
31
  (function (LogtoTenantConfigKey) {
26
32
  LogtoTenantConfigKey["AdminConsole"] = "adminConsole";
33
+ LogtoTenantConfigKey["CloudConnection"] = "cloudConnection";
27
34
  /** The URL to redirect when session not found in Sign-in Experience. */
28
35
  LogtoTenantConfigKey["SessionNotFoundRedirectUrl"] = "sessionNotFoundRedirectUrl";
29
36
  })(LogtoTenantConfigKey || (LogtoTenantConfigKey = {}));
30
37
  export const logtoTenantConfigGuard = Object.freeze({
31
38
  [LogtoTenantConfigKey.AdminConsole]: adminConsoleDataGuard,
39
+ [LogtoTenantConfigKey.CloudConnection]: cloudConnectionDataGuard,
32
40
  [LogtoTenantConfigKey.SessionNotFoundRedirectUrl]: z.object({ url: z.string() }),
33
41
  });
34
42
  export const logtoConfigKeys = Object.freeze([
@@ -67,87 +67,48 @@ export declare const storageProviderGuard: Readonly<{
67
67
  export declare enum EmailServiceProvider {
68
68
  SendGrid = "SendGrid"
69
69
  }
70
- /**
71
- * `General` is now used as a fallback scenario.
72
- * This will be extended in the future since we will send different emails for
73
- * different purposes (such as webhook that inform users of suspicious account activities).
74
- */
75
- export declare enum OtherEmailTemplate {
76
- General = "General"
77
- }
78
- export declare const otherEmailTemplateGuard: z.ZodNativeEnum<typeof OtherEmailTemplate>;
79
70
  export declare const sendgridEmailServiceDataGuard: z.ZodObject<{
80
- fromName: z.ZodString;
81
- fromEmail: z.ZodString;
82
- templates: z.ZodRecord<z.ZodUnion<[z.ZodNativeEnum<typeof import("@logto/connector-kit").VerificationCodeType>, z.ZodNativeEnum<typeof OtherEmailTemplate>]>, z.ZodObject<{
83
- subject: z.ZodString;
84
- content: z.ZodString;
85
- }, "strip", z.ZodTypeAny, {
86
- subject: string;
87
- content: string;
88
- }, {
89
- subject: string;
90
- content: string;
91
- }>>;
92
71
  provider: z.ZodLiteral<EmailServiceProvider>;
93
72
  apiKey: z.ZodString;
73
+ templateId: z.ZodString;
74
+ fromName: z.ZodString;
75
+ fromEmail: z.ZodString;
94
76
  }, "strip", z.ZodTypeAny, {
95
77
  provider: EmailServiceProvider;
96
78
  apiKey: string;
79
+ templateId: string;
97
80
  fromName: string;
98
81
  fromEmail: string;
99
- templates: Partial<Record<OtherEmailTemplate.General | import("@logto/connector-kit").VerificationCodeType, {
100
- subject: string;
101
- content: string;
102
- }>>;
103
82
  }, {
104
83
  provider: EmailServiceProvider;
105
84
  apiKey: string;
85
+ templateId: string;
106
86
  fromName: string;
107
87
  fromEmail: string;
108
- templates: Partial<Record<OtherEmailTemplate.General | import("@logto/connector-kit").VerificationCodeType, {
109
- subject: string;
110
- content: string;
111
- }>>;
112
88
  }>;
113
89
  export type SendgridEmailServiceData = z.infer<typeof sendgridEmailServiceDataGuard>;
114
90
  export declare const emailServiceDataGuard: z.ZodDiscriminatedUnion<"provider", [z.ZodObject<{
115
- fromName: z.ZodString;
116
- fromEmail: z.ZodString;
117
- templates: z.ZodRecord<z.ZodUnion<[z.ZodNativeEnum<typeof import("@logto/connector-kit").VerificationCodeType>, z.ZodNativeEnum<typeof OtherEmailTemplate>]>, z.ZodObject<{
118
- subject: z.ZodString;
119
- content: z.ZodString;
120
- }, "strip", z.ZodTypeAny, {
121
- subject: string;
122
- content: string;
123
- }, {
124
- subject: string;
125
- content: string;
126
- }>>;
127
91
  provider: z.ZodLiteral<EmailServiceProvider>;
128
92
  apiKey: z.ZodString;
93
+ templateId: z.ZodString;
94
+ fromName: z.ZodString;
95
+ fromEmail: z.ZodString;
129
96
  }, "strip", z.ZodTypeAny, {
130
97
  provider: EmailServiceProvider;
131
98
  apiKey: string;
99
+ templateId: string;
132
100
  fromName: string;
133
101
  fromEmail: string;
134
- templates: Partial<Record<OtherEmailTemplate.General | import("@logto/connector-kit").VerificationCodeType, {
135
- subject: string;
136
- content: string;
137
- }>>;
138
102
  }, {
139
103
  provider: EmailServiceProvider;
140
104
  apiKey: string;
105
+ templateId: string;
141
106
  fromName: string;
142
107
  fromEmail: string;
143
- templates: Partial<Record<OtherEmailTemplate.General | import("@logto/connector-kit").VerificationCodeType, {
144
- subject: string;
145
- content: string;
146
- }>>;
147
108
  }>]>;
148
109
  export type EmailServiceData = z.infer<typeof emailServiceDataGuard>;
149
110
  export declare enum EmailServiceProviderKey {
150
- EmailServiceProvider = "EmailServiceProvider"
111
+ EmailServiceProvider = "emailServiceProvider"
151
112
  }
152
113
  export type EmailServiceProviderType = {
153
114
  [EmailServiceProviderKey.EmailServiceProvider]: EmailServiceData;
@@ -1,4 +1,3 @@
1
- import { verificationCodeTypeGuard } from '@logto/connector-kit';
2
1
  import { z } from 'zod';
3
2
  // Alteration state
4
3
  export var AlterationStateKey;
@@ -48,35 +47,19 @@ export var EmailServiceProvider;
48
47
  (function (EmailServiceProvider) {
49
48
  EmailServiceProvider["SendGrid"] = "SendGrid";
50
49
  })(EmailServiceProvider || (EmailServiceProvider = {}));
51
- /**
52
- * `General` is now used as a fallback scenario.
53
- * This will be extended in the future since we will send different emails for
54
- * different purposes (such as webhook that inform users of suspicious account activities).
55
- */
56
- export var OtherEmailTemplate;
57
- (function (OtherEmailTemplate) {
58
- OtherEmailTemplate["General"] = "General";
59
- })(OtherEmailTemplate || (OtherEmailTemplate = {}));
60
- export const otherEmailTemplateGuard = z.nativeEnum(OtherEmailTemplate);
61
- const emailServiceBasicConfig = {
62
- fromName: z.string(),
63
- fromEmail: z.string(),
64
- templates: z.record(verificationCodeTypeGuard.or(otherEmailTemplateGuard), z.object({
65
- subject: z.string(),
66
- content: z.string(),
67
- })),
68
- };
69
50
  export const sendgridEmailServiceDataGuard = z.object({
70
51
  provider: z.literal(EmailServiceProvider.SendGrid),
71
52
  apiKey: z.string(),
72
- ...emailServiceBasicConfig,
53
+ templateId: z.string(),
54
+ fromName: z.string(),
55
+ fromEmail: z.string(),
73
56
  });
74
57
  export const emailServiceDataGuard = z.discriminatedUnion('provider', [
75
58
  sendgridEmailServiceDataGuard,
76
59
  ]);
77
60
  export var EmailServiceProviderKey;
78
61
  (function (EmailServiceProviderKey) {
79
- EmailServiceProviderKey["EmailServiceProvider"] = "EmailServiceProvider";
62
+ EmailServiceProviderKey["EmailServiceProvider"] = "emailServiceProvider";
80
63
  })(EmailServiceProviderKey || (EmailServiceProviderKey = {}));
81
64
  export const emailServiceProviderGuard = Object.freeze({
82
65
  [EmailServiceProviderKey.EmailServiceProvider]: emailServiceDataGuard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/schemas",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "author": "Silverhand Inc. <contact@silverhand.io>",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",
@@ -24,20 +24,20 @@
24
24
  "node": "^18.12.0"
25
25
  },
26
26
  "devDependencies": {
27
- "@silverhand/eslint-config": "3.0.1",
27
+ "@silverhand/eslint-config": "4.0.1",
28
28
  "@silverhand/essentials": "^2.5.0",
29
- "@silverhand/ts-config": "3.0.0",
29
+ "@silverhand/ts-config": "4.0.0",
30
30
  "@types/inquirer": "^9.0.0",
31
31
  "@types/jest": "^29.4.0",
32
32
  "@types/node": "^18.11.18",
33
33
  "@types/pluralize": "^0.0.29",
34
- "camelcase": "^7.0.0",
34
+ "camelcase": "^8.0.0",
35
35
  "chalk": "^5.0.0",
36
- "eslint": "^8.34.0",
36
+ "eslint": "^8.44.0",
37
37
  "jest": "^29.5.0",
38
- "lint-staged": "^13.0.0",
38
+ "lint-staged": "^14.0.0",
39
39
  "pluralize": "^8.0.0",
40
- "prettier": "^2.8.2",
40
+ "prettier": "^3.0.0",
41
41
  "roarr": "^7.11.0",
42
42
  "slonik": "^30.0.0",
43
43
  "slonik-sql-tag-raw": "^1.1.4",
@@ -70,7 +70,7 @@
70
70
  "@logto/phrases": "^1.4.1",
71
71
  "@logto/phrases-ui": "^1.2.0",
72
72
  "@logto/shared": "^2.0.0",
73
- "@withtyped/server": "^0.11.1"
73
+ "@withtyped/server": "^0.12.9"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "zod": "^3.20.2"
@@ -0,0 +1,13 @@
1
+ create table daily_active_users (
2
+ id varchar(21) not null,
3
+ tenant_id varchar(21) not null
4
+ references tenants (id) on update cascade on delete cascade,
5
+ user_id varchar(21) not null,
6
+ date timestamptz not null,
7
+ primary key (id),
8
+ constraint daily_active_users__user_id_date
9
+ unique (user_id, date)
10
+ );
11
+
12
+ create index daily_active_users__id
13
+ on daily_active_users (tenant_id, id);