@logto/schemas 1.5.0 → 1.7.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/LICENSE +0 -6
- package/alterations/1.6.0-1685691718-domain-unique.ts +20 -0
- package/alterations/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.ts +85 -0
- package/alterations/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.ts +88 -0
- package/alterations/1.7.0-1688627407-daily-active-users.ts +61 -0
- package/alterations-js/1.6.0-1685691718-domain-unique.d.ts +3 -0
- package/alterations-js/1.6.0-1685691718-domain-unique.js +16 -0
- package/alterations-js/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.d.ts +3 -0
- package/alterations-js/1.7.0-1688375200-sync-cloud-m2m-to-logto-config.js +42 -0
- package/alterations-js/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.d.ts +3 -0
- package/alterations-js/1.7.0-1688613459-remove-m2m-credentials-from-existing-logto-email-connector-config.js +55 -0
- package/alterations-js/1.7.0-1688627407-daily-active-users.d.ts +3 -0
- package/alterations-js/1.7.0-1688627407-daily-active-users.js +53 -0
- package/lib/consts/date.d.ts +5 -0
- package/lib/consts/date.js +1 -0
- package/lib/consts/index.d.ts +2 -0
- package/lib/consts/index.js +2 -0
- package/lib/consts/oidc.d.ts +5 -0
- package/lib/consts/oidc.js +6 -0
- package/lib/consts/tenant.d.ts +1 -0
- package/lib/consts/tenant.js +1 -0
- package/lib/db-entries/daily-active-user.d.ts +14 -0
- package/lib/db-entries/daily-active-user.js +32 -0
- package/lib/db-entries/domain.d.ts +3 -3
- package/lib/db-entries/domain.js +3 -3
- package/lib/db-entries/index.d.ts +1 -0
- package/lib/db-entries/index.js +1 -0
- package/lib/foundations/jsonb-types.d.ts +22 -38
- package/lib/foundations/jsonb-types.js +18 -3
- package/lib/models/tenants.d.ts +34 -3
- package/lib/models/tenants.js +16 -4
- package/lib/seeds/cloud-api.d.ts +5 -1
- package/lib/seeds/cloud-api.js +4 -0
- package/lib/seeds/logto-config.d.ts +6 -1
- package/lib/seeds/logto-config.js +10 -0
- package/lib/seeds/tenant.d.ts +1 -3
- package/lib/types/connector.d.ts +17 -5
- package/lib/types/connector.js +2 -0
- package/lib/types/domain.d.ts +3 -25
- package/lib/types/domain.js +0 -7
- package/lib/types/index.d.ts +0 -1
- package/lib/types/index.js +0 -1
- package/lib/types/logto-config.d.ts +16 -0
- package/lib/types/logto-config.js +8 -0
- package/lib/types/system.d.ts +55 -6
- package/lib/types/system.js +25 -2
- package/package.json +10 -8
- package/tables/daily_active_users.sql +13 -0
- package/tables/domains.sql +2 -2
- package/lib/types/tenant.d.ts +0 -37
- package/lib/types/tenant.js +0 -19
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,20 @@
|
|
|
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 domains drop constraint domains__domain;
|
|
9
|
+
alter table domains add constraint domains__domain unique (tenant_id, domain);
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table domains drop constraint domains__domain;
|
|
15
|
+
alter table domains add constraint domains__domain unique (domain);
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default alteration;
|
|
@@ -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,16 @@
|
|
|
1
|
+
import { sql } from 'slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table domains drop constraint domains__domain;
|
|
6
|
+
alter table domains add constraint domains__domain unique (tenant_id, domain);
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table domains drop constraint domains__domain;
|
|
12
|
+
alter table domains add constraint domains__domain unique (domain);
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
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,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,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 @@
|
|
|
1
|
+
export const inSeconds = Object.freeze({ oneMinute: 60, oneHour: 60 * 60, oneDay: 24 * 60 * 60 });
|
package/lib/consts/index.d.ts
CHANGED
package/lib/consts/index.js
CHANGED
package/lib/consts/oidc.d.ts
CHANGED
package/lib/consts/oidc.js
CHANGED
|
@@ -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
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DomainDnsRecords, CloudflareData, GeneratedSchema } from './../foundations/index.js';
|
|
1
|
+
import { DomainStatus, DomainDnsRecords, CloudflareData, GeneratedSchema } from './../foundations/index.js';
|
|
2
2
|
export type CreateDomain = {
|
|
3
3
|
tenantId?: string;
|
|
4
4
|
id: string;
|
|
5
5
|
domain: string;
|
|
6
|
-
status?:
|
|
6
|
+
status?: DomainStatus;
|
|
7
7
|
errorMessage?: string | null;
|
|
8
8
|
dnsRecords?: DomainDnsRecords;
|
|
9
9
|
cloudflareData?: CloudflareData | null;
|
|
@@ -14,7 +14,7 @@ export type Domain = {
|
|
|
14
14
|
tenantId: string;
|
|
15
15
|
id: string;
|
|
16
16
|
domain: string;
|
|
17
|
-
status:
|
|
17
|
+
status: DomainStatus;
|
|
18
18
|
errorMessage: string | null;
|
|
19
19
|
dnsRecords: DomainDnsRecords;
|
|
20
20
|
cloudflareData: CloudflareData | null;
|
package/lib/db-entries/domain.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { domainDnsRecordsGuard, cloudflareDataGuard } from './../foundations/index.js';
|
|
3
|
+
import { domainStatusGuard, domainDnsRecordsGuard, cloudflareDataGuard } from './../foundations/index.js';
|
|
4
4
|
const createGuard = z.object({
|
|
5
5
|
tenantId: z.string().max(21).optional(),
|
|
6
6
|
id: z.string().min(1).max(21),
|
|
7
7
|
domain: z.string().min(1).max(256),
|
|
8
|
-
status:
|
|
8
|
+
status: domainStatusGuard.optional(),
|
|
9
9
|
errorMessage: z.string().max(1024).nullable().optional(),
|
|
10
10
|
dnsRecords: domainDnsRecordsGuard.optional(),
|
|
11
11
|
cloudflareData: cloudflareDataGuard.nullable().optional(),
|
|
@@ -16,7 +16,7 @@ const guard = z.object({
|
|
|
16
16
|
tenantId: z.string().max(21),
|
|
17
17
|
id: z.string().min(1).max(21),
|
|
18
18
|
domain: z.string().min(1).max(256),
|
|
19
|
-
status:
|
|
19
|
+
status: domainStatusGuard,
|
|
20
20
|
errorMessage: z.string().max(1024).nullable(),
|
|
21
21
|
dnsRecords: domainDnsRecordsGuard,
|
|
22
22
|
cloudflareData: cloudflareDataGuard.nullable(),
|
|
@@ -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';
|
package/lib/db-entries/index.js
CHANGED
|
@@ -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';
|