@logto/schemas 1.34.0 → 1.36.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/alterations/1.35.0-1764580455-remove-daily-active-users-foreign-key.ts +69 -0
- package/alterations/1.35.0-1764580589-create-aggregated-daily-active-users-table.ts +51 -0
- package/alterations/1.35.0-1764653048-update-daily-token-usage-mau-support.ts +37 -0
- package/alterations/1.35.0-1765183934-add-logs-created-at-id-index.ts +39 -0
- package/alterations/1.35.0-1765255453-update-saml-session-relay-state-to-varchar-512.ts +31 -0
- package/alterations/1.35.0-1765631949-drop-redundant-logs-id-index.ts +47 -0
- package/alterations/1.35.0-1766028646-grant-tenants-table-tag-column-read-permission.ts +39 -0
- package/alterations/1.36.0-1767193412-allow-token-exchange.ts +34 -0
- package/alterations/1.36.0-1767859553-passkey-sign-in.ts +21 -0
- package/alterations/1.36.0-1768192304-enable-account-center-for-admin-tenant.ts +32 -0
- package/alterations/1.36.0-1768464306-enable-mfa-for-admin-tenant.ts +30 -0
- package/alterations/1.36.0-1768758295-add-user-geo-location.ts +32 -0
- package/alterations/1.36.0-1768891516-add-user-sign-in-countries-table.ts +33 -0
- package/alterations/1.36.0-1769067642-add-adaptive-mfa-configuration.ts +19 -0
- package/alterations/1.36.0-1769172677-enable-organization-mfa-policy-for-admin-tenant.ts +31 -0
- package/alterations-js/1.35.0-1764580455-remove-daily-active-users-foreign-key.js +57 -0
- package/alterations-js/1.35.0-1764580589-create-aggregated-daily-active-users-table.js +40 -0
- package/alterations-js/1.35.0-1764653048-update-daily-token-usage-mau-support.js +31 -0
- package/alterations-js/1.35.0-1765183934-add-logs-created-at-id-index.js +35 -0
- package/alterations-js/1.35.0-1765255453-update-saml-session-relay-state-to-varchar-512.js +25 -0
- package/alterations-js/1.35.0-1765631949-drop-redundant-logs-id-index.js +43 -0
- package/alterations-js/1.35.0-1766028646-grant-tenants-table-tag-column-read-permission.js +31 -0
- package/alterations-js/1.36.0-1767193412-allow-token-exchange.js +30 -0
- package/alterations-js/1.36.0-1767859553-passkey-sign-in.js +17 -0
- package/alterations-js/1.36.0-1768192304-enable-account-center-for-admin-tenant.js +27 -0
- package/alterations-js/1.36.0-1768464306-enable-mfa-for-admin-tenant.js +25 -0
- package/alterations-js/1.36.0-1768758295-add-user-geo-location.js +27 -0
- package/alterations-js/1.36.0-1768891516-add-user-sign-in-countries-table.js +28 -0
- package/alterations-js/1.36.0-1769067642-add-adaptive-mfa-configuration.js +15 -0
- package/alterations-js/1.36.0-1769172677-enable-organization-mfa-policy-for-admin-tenant.js +26 -0
- package/lib/consts/product-event.d.ts +0 -12
- package/lib/consts/product-event.js +0 -13
- package/lib/db-entries/aggregated-daily-active-user.d.ts +22 -0
- package/lib/db-entries/aggregated-daily-active-user.js +33 -0
- package/lib/db-entries/daily-token-usage.d.ts +5 -1
- package/lib/db-entries/daily-token-usage.js +8 -0
- package/lib/db-entries/index.d.ts +3 -0
- package/lib/db-entries/index.js +3 -0
- package/lib/db-entries/saml-application-session.js +2 -2
- package/lib/db-entries/sign-in-experience.d.ts +6 -2
- package/lib/db-entries/sign-in-experience.js +9 -1
- package/lib/db-entries/user-geo-location.d.ts +24 -0
- package/lib/db-entries/user-geo-location.js +37 -0
- package/lib/db-entries/user-sign-in-country.d.ts +24 -0
- package/lib/db-entries/user-sign-in-country.js +33 -0
- package/lib/foundations/jsonb-types/account-centers.d.ts +2 -2
- package/lib/foundations/jsonb-types/captcha.d.ts +16 -0
- package/lib/foundations/jsonb-types/captcha.js +7 -0
- package/lib/foundations/jsonb-types/logs.d.ts +703 -0
- package/lib/foundations/jsonb-types/logs.js +52 -0
- package/lib/foundations/jsonb-types/oidc-module.d.ts +15 -3
- package/lib/foundations/jsonb-types/oidc-module.js +15 -3
- package/lib/foundations/jsonb-types/saml-application-configs.d.ts +1 -1
- package/lib/foundations/jsonb-types/sentinel.d.ts +13 -1
- package/lib/foundations/jsonb-types/sentinel.js +12 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +59 -0
- package/lib/foundations/jsonb-types/sign-in-experience.js +11 -0
- package/lib/seeds/account-center.d.ts +6 -0
- package/lib/seeds/account-center.js +24 -0
- package/lib/seeds/cloud-api.d.ts +3 -1
- package/lib/seeds/cloud-api.js +2 -0
- package/lib/seeds/sign-in-experience.js +6 -1
- package/lib/types/alteration.d.ts +11 -1
- package/lib/types/application.d.ts +6 -0
- package/lib/types/consent.d.ts +4 -0
- package/lib/types/custom-profile-fields.d.ts +3 -3
- package/lib/types/hook.d.ts +2 -2
- package/lib/types/interactions.d.ts +16 -7
- package/lib/types/interactions.js +10 -4
- package/lib/types/log/index.d.ts +12 -6
- package/lib/types/log/interaction.d.ts +5 -1
- package/lib/types/logto-config/index.d.ts +9 -9
- package/lib/types/logto-config/jwt-customizer.d.ts +17 -17
- package/lib/types/saml-application.d.ts +7 -7
- package/lib/types/sign-in-experience.d.ts +19 -1
- package/lib/types/sign-in-experience.js +3 -1
- package/lib/types/user.d.ts +10 -7
- package/lib/types/user.js +1 -0
- package/package.json +6 -6
- package/tables/_after_all.sql +1 -1
- package/tables/aggregated_daily_active_users.sql +16 -0
- package/tables/daily_active_users.sql +9 -4
- package/tables/daily_token_usage.sql +3 -2
- package/tables/logs.sql +3 -3
- package/tables/saml_application_sessions.sql +1 -1
- package/tables/sign_in_experiences.sql +2 -0
- package/tables/user_geo_locations.sql +14 -0
- package/tables/user_sign_in_countries.sql +16 -0
- package/tables/users.sql +3 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Remove foreign key constraint from daily_active_users table to allow
|
|
7
|
+
* historical billing data to persist even after tenant deletion.
|
|
8
|
+
* This supports the MAU-based billing system requirements.
|
|
9
|
+
*
|
|
10
|
+
* Index optimizations:
|
|
11
|
+
* 1. Removes redundant (tenant_id, id) index (id is already primary key)
|
|
12
|
+
* 2. Adds optimized index (tenant_id, date, user_id) for aggregation queries
|
|
13
|
+
* 3. Replaces problematic partial index with BRIN index
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const alteration: AlterationScript = {
|
|
17
|
+
up: async (pool) => {
|
|
18
|
+
// Drop the existing foreign key constraint
|
|
19
|
+
await pool.query(sql`
|
|
20
|
+
alter table daily_active_users
|
|
21
|
+
drop constraint if exists daily_active_users_tenant_id_fkey
|
|
22
|
+
`);
|
|
23
|
+
|
|
24
|
+
// Remove the redundant (tenant_id, id) index since id is already primary key
|
|
25
|
+
await pool.query(sql`
|
|
26
|
+
drop index if exists daily_active_users__id
|
|
27
|
+
`);
|
|
28
|
+
|
|
29
|
+
// Add optimized index for aggregation queries with better write performance
|
|
30
|
+
await pool.query(sql`
|
|
31
|
+
create index daily_active_users__tenant_date_user
|
|
32
|
+
on daily_active_users (tenant_id, date, user_id)
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
// Add BRIN index for time-series date range queries
|
|
36
|
+
// Optimized for sequential data insertion and range scans (date >= ?)
|
|
37
|
+
await pool.query(sql`
|
|
38
|
+
create index daily_active_users__date_brin
|
|
39
|
+
on daily_active_users using brin (date)
|
|
40
|
+
`);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
down: async (pool) => {
|
|
44
|
+
// Drop the new indexes we created
|
|
45
|
+
await pool.query(sql`
|
|
46
|
+
drop index if exists daily_active_users__date_brin
|
|
47
|
+
`);
|
|
48
|
+
|
|
49
|
+
await pool.query(sql`
|
|
50
|
+
drop index if exists daily_active_users__tenant_date_user
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
// Recreate the original redundant index
|
|
54
|
+
await pool.query(sql`
|
|
55
|
+
create index daily_active_users__id
|
|
56
|
+
on daily_active_users (tenant_id, id)
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
// Recreate the foreign key constraint
|
|
60
|
+
await pool.query(sql`
|
|
61
|
+
alter table daily_active_users
|
|
62
|
+
add constraint daily_active_users_tenant_id_fkey
|
|
63
|
+
foreign key (tenant_id) references tenants(id)
|
|
64
|
+
on update cascade on delete cascade
|
|
65
|
+
`);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default alteration;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create aggregated_daily_active_users table for MAU-based billing system.
|
|
9
|
+
* This table consolidates daily user activities for efficient billing calculations.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const alteration: AlterationScript = {
|
|
13
|
+
up: async (pool) => {
|
|
14
|
+
// Create the aggregated daily active users table
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
create table aggregated_daily_active_users (
|
|
17
|
+
tenant_id varchar(21) not null,
|
|
18
|
+
activity_date date not null,
|
|
19
|
+
user_id varchar(21) not null,
|
|
20
|
+
activity_count integer not null,
|
|
21
|
+
primary key (tenant_id, activity_date, user_id)
|
|
22
|
+
);
|
|
23
|
+
`);
|
|
24
|
+
|
|
25
|
+
// Index for billing cycle range queries
|
|
26
|
+
await pool.query(sql`
|
|
27
|
+
create index aggregated_daily_active_users__tenant_date
|
|
28
|
+
on aggregated_daily_active_users (tenant_id, activity_date);
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
// Index for tenant-specific user activity queries
|
|
32
|
+
await pool.query(sql`
|
|
33
|
+
create index aggregated_daily_active_users__tenant_user_date
|
|
34
|
+
on aggregated_daily_active_users (tenant_id, user_id, activity_date desc);
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
await applyTableRls(pool, 'aggregated_daily_active_users');
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
down: async (pool) => {
|
|
41
|
+
// Drop RLS policies first
|
|
42
|
+
await dropTableRls(pool, 'aggregated_daily_active_users');
|
|
43
|
+
|
|
44
|
+
// Drop the table and all its indexes
|
|
45
|
+
await pool.query(sql`
|
|
46
|
+
drop table aggregated_daily_active_users;
|
|
47
|
+
`);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default alteration;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
up: async (pool) => {
|
|
7
|
+
// Add new columns for user and m2m token usage tracking
|
|
8
|
+
await pool.query(sql`
|
|
9
|
+
alter table daily_token_usage
|
|
10
|
+
add column user_token_usage bigint not null default 0,
|
|
11
|
+
add column m2m_token_usage bigint not null default 0;
|
|
12
|
+
`);
|
|
13
|
+
|
|
14
|
+
// Remove foreign key constraint to support enterprise tenant deletion requirements
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
alter table daily_token_usage
|
|
17
|
+
drop constraint if exists daily_token_usage_tenant_id_fkey;
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async (pool) => {
|
|
21
|
+
// Remove the new columns
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
alter table daily_token_usage
|
|
24
|
+
drop column if exists user_token_usage,
|
|
25
|
+
drop column if exists m2m_token_usage;
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
// Re-add the foreign key constraint
|
|
29
|
+
await pool.query(sql`
|
|
30
|
+
alter table daily_token_usage
|
|
31
|
+
add constraint daily_token_usage_tenant_id_fkey
|
|
32
|
+
foreign key (tenant_id) references tenants (id) on update cascade on delete cascade;
|
|
33
|
+
`);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default alteration;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
beforeUp: async (pool) => {
|
|
7
|
+
/**
|
|
8
|
+
* Use 'if not exists' to ensure idempotency.
|
|
9
|
+
* If the subsequent transaction in 'up' fails, the migration can be safely re-run without failing on an already created index.
|
|
10
|
+
*/
|
|
11
|
+
await pool.query(sql`
|
|
12
|
+
create index concurrently if not exists logs__created_at_id
|
|
13
|
+
on logs (tenant_id, created_at, id);
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
up: async () => {
|
|
17
|
+
/**
|
|
18
|
+
* The index on the logs table must be created outside of a transaction to avoid table locks.
|
|
19
|
+
* 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty.
|
|
20
|
+
*/
|
|
21
|
+
},
|
|
22
|
+
beforeDown: async (pool) => {
|
|
23
|
+
/**
|
|
24
|
+
* Use 'if exists' to ensure idempotency. If the subsequent transaction in 'down' fails,
|
|
25
|
+
* the rollback can be safely re-run without failing on a non-existent index.
|
|
26
|
+
*/
|
|
27
|
+
await pool.query(sql`
|
|
28
|
+
drop index concurrently if exists logs__created_at_id;
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
down: async () => {
|
|
32
|
+
/**
|
|
33
|
+
* The index on the logs table must be dropped outside of a transaction to avoid table locks.
|
|
34
|
+
* 'concurrently' cannot be used inside a transaction, so this down is intentionally left empty.
|
|
35
|
+
*/
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default alteration;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Update saml_application_sessions.relay_state column type from varchar(256) to text
|
|
7
|
+
* to support longer relay state values that may be used in SAML authentication flows.
|
|
8
|
+
*
|
|
9
|
+
* The relay state parameter in SAML can contain arbitrary data that needs to be
|
|
10
|
+
* preserved and returned to the service provider, and 256 characters may not be
|
|
11
|
+
* sufficient for all use cases.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const alteration: AlterationScript = {
|
|
15
|
+
up: async (pool) => {
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
alter table saml_application_sessions
|
|
18
|
+
alter column relay_state type varchar(512)
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
down: async (pool) => {
|
|
23
|
+
// The down migration may fail or cause data loss if any existing relay_state values exceed 256 characters. Consider adding a USING clause to safely truncate values during rollback, this ensures the rollback operation won't fail due to data that's too long for the varchar(256) constraint.
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
alter table saml_application_sessions
|
|
26
|
+
alter column relay_state type varchar(256) using left(relay_state, 256)
|
|
27
|
+
`);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default alteration;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The `logs__id` index on (tenant_id, id) is redundant because:
|
|
7
|
+
* 1. `id` is already the primary key, which can efficiently lookup by id
|
|
8
|
+
* 2. `logs__created_at_id` index on (tenant_id, created_at, id) already covers tenant_id queries
|
|
9
|
+
*
|
|
10
|
+
* Removing this index reduces write IOPS during log deletion operations,
|
|
11
|
+
* as each DELETE no longer needs to update this redundant index.
|
|
12
|
+
*/
|
|
13
|
+
const alteration: AlterationScript = {
|
|
14
|
+
beforeUp: async (pool) => {
|
|
15
|
+
/**
|
|
16
|
+
* Use 'if exists' to ensure idempotency.
|
|
17
|
+
* 'concurrently' avoids table locks during index drop.
|
|
18
|
+
*/
|
|
19
|
+
await pool.query(sql`
|
|
20
|
+
drop index concurrently if exists logs__id;
|
|
21
|
+
`);
|
|
22
|
+
},
|
|
23
|
+
up: async () => {
|
|
24
|
+
/**
|
|
25
|
+
* The index must be dropped outside of a transaction to avoid table locks.
|
|
26
|
+
* 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty.
|
|
27
|
+
*/
|
|
28
|
+
},
|
|
29
|
+
beforeDown: async (pool) => {
|
|
30
|
+
/**
|
|
31
|
+
* Recreate the index if rolling back.
|
|
32
|
+
* Use 'if not exists' to ensure idempotency.
|
|
33
|
+
*/
|
|
34
|
+
await pool.query(sql`
|
|
35
|
+
create index concurrently if not exists logs__id
|
|
36
|
+
on logs (tenant_id, id);
|
|
37
|
+
`);
|
|
38
|
+
},
|
|
39
|
+
down: async () => {
|
|
40
|
+
/**
|
|
41
|
+
* The index must be created outside of a transaction to avoid table locks.
|
|
42
|
+
* 'concurrently' cannot be used inside a transaction, so this down is intentionally left empty.
|
|
43
|
+
*/
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default alteration;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type CommonQueryMethods, sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
6
|
+
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
7
|
+
select current_database();
|
|
8
|
+
`);
|
|
9
|
+
|
|
10
|
+
return currentDatabase.replaceAll('-', '_');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Grant read permission to the tag column in the tenants table to the logto_tenant_<databaseName> role.
|
|
15
|
+
*/
|
|
16
|
+
const alteration: AlterationScript = {
|
|
17
|
+
up: async (pool) => {
|
|
18
|
+
const databaseName = await getDatabaseName(pool);
|
|
19
|
+
const baseRoleId = sql.identifier([`logto_tenant_${databaseName}`]);
|
|
20
|
+
|
|
21
|
+
await pool.query(sql`
|
|
22
|
+
grant select (tag)
|
|
23
|
+
on table tenants
|
|
24
|
+
to ${baseRoleId}
|
|
25
|
+
`);
|
|
26
|
+
},
|
|
27
|
+
down: async (pool) => {
|
|
28
|
+
const databaseName = await getDatabaseName(pool);
|
|
29
|
+
const baseRoleId = sql.identifier([`logto_tenant_${databaseName}`]);
|
|
30
|
+
|
|
31
|
+
await pool.query(sql`
|
|
32
|
+
revoke select (tag)
|
|
33
|
+
on table tenants
|
|
34
|
+
from ${baseRoleId}
|
|
35
|
+
`);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default alteration;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
up: async (pool) => {
|
|
7
|
+
/**
|
|
8
|
+
* For backward compatibility, set allowTokenExchange = true for existing first-party
|
|
9
|
+
* Traditional, Native, and SPA applications.
|
|
10
|
+
* M2M applications were never allowed to use token exchange before this feature.
|
|
11
|
+
*
|
|
12
|
+
* The admin-console should keep token exchange disabled, so we skip it here.
|
|
13
|
+
*/
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
update applications
|
|
16
|
+
set custom_client_metadata = custom_client_metadata || '{"allowTokenExchange": true}'::jsonb
|
|
17
|
+
where is_third_party = false
|
|
18
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
19
|
+
and id != 'admin-console';
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
down: async (pool) => {
|
|
23
|
+
// Remove allowTokenExchange only from applications that were updated in the `up` migration
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
update applications
|
|
26
|
+
set custom_client_metadata = custom_client_metadata - 'allowTokenExchange'
|
|
27
|
+
where is_third_party = false
|
|
28
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
29
|
+
and id != 'admin-console';
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '@silverhand/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 sign_in_experiences
|
|
9
|
+
add column passkey_sign_in jsonb /* @use PasskeySignIn */ not null default '{}'::jsonb;
|
|
10
|
+
create index users_mfa_verifications_gin on users using gin (mfa_verifications jsonb_path_ops);
|
|
11
|
+
`);
|
|
12
|
+
},
|
|
13
|
+
down: async (pool) => {
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
alter table sign_in_experiences drop column passkey_sign_in;
|
|
16
|
+
drop index users_mfa_verifications_gin;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable the account center for the admin tenant and set all fields to Edit.
|
|
9
|
+
* This allows the console profile page to use the Account API.
|
|
10
|
+
*/
|
|
11
|
+
const alteration: AlterationScript = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
update account_centers
|
|
15
|
+
set enabled = true,
|
|
16
|
+
fields = '{"name": "Edit", "avatar": "Edit", "profile": "Edit", "email": "Edit", "phone": "Edit", "password": "Edit", "username": "Edit", "social": "Edit", "customData": "Edit", "mfa": "Edit"}'::jsonb
|
|
17
|
+
where tenant_id = ${adminTenantId}
|
|
18
|
+
and id = 'default'
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
down: async (pool) => {
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
update account_centers
|
|
24
|
+
set enabled = false,
|
|
25
|
+
fields = '{}'::jsonb
|
|
26
|
+
where tenant_id = ${adminTenantId}
|
|
27
|
+
and id = 'default'
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable MFA (TOTP and WebAuthn) for the admin tenant with NoPrompt policy.
|
|
9
|
+
* This allows users to optionally set up MFA via the account center.
|
|
10
|
+
*/
|
|
11
|
+
const alteration: AlterationScript = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
update sign_in_experiences
|
|
15
|
+
set mfa = '{"factors":["Totp","WebAuthn","BackupCode"],"policy":"NoPrompt"}'::jsonb
|
|
16
|
+
where tenant_id = ${adminTenantId}
|
|
17
|
+
and id = 'default'
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async (pool) => {
|
|
21
|
+
await pool.query(sql`
|
|
22
|
+
update sign_in_experiences
|
|
23
|
+
set mfa = '{"factors":[],"policy":"UserControlled"}'::jsonb
|
|
24
|
+
where tenant_id = ${adminTenantId}
|
|
25
|
+
and id = 'default'
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create table user_geo_locations (
|
|
11
|
+
tenant_id varchar(21) not null
|
|
12
|
+
references tenants (id) on update cascade on delete cascade,
|
|
13
|
+
user_id varchar(12) not null
|
|
14
|
+
references users (id) on update cascade on delete cascade,
|
|
15
|
+
latitude numeric(9,6),
|
|
16
|
+
longitude numeric(9,6),
|
|
17
|
+
updated_at timestamptz not null default now(),
|
|
18
|
+
primary key (tenant_id, user_id),
|
|
19
|
+
check ((latitude is null) = (longitude is null))
|
|
20
|
+
);
|
|
21
|
+
`);
|
|
22
|
+
await applyTableRls(pool, 'user_geo_locations');
|
|
23
|
+
},
|
|
24
|
+
down: async (pool) => {
|
|
25
|
+
await dropTableRls(pool, 'user_geo_locations');
|
|
26
|
+
await pool.query(sql`
|
|
27
|
+
drop table user_geo_locations;
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create table user_sign_in_countries (
|
|
11
|
+
tenant_id varchar(21) not null
|
|
12
|
+
references tenants (id) on update cascade on delete cascade,
|
|
13
|
+
user_id varchar(12) not null
|
|
14
|
+
references users (id) on update cascade on delete cascade,
|
|
15
|
+
country varchar(16) not null,
|
|
16
|
+
last_sign_in_at timestamptz not null default(now()),
|
|
17
|
+
primary key (tenant_id, user_id, country)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
create index user_sign_in_countries__tenant_user_last_sign_in_at
|
|
21
|
+
on user_sign_in_countries (tenant_id, user_id, last_sign_in_at desc);
|
|
22
|
+
`);
|
|
23
|
+
await applyTableRls(pool, 'user_sign_in_countries');
|
|
24
|
+
},
|
|
25
|
+
down: async (pool) => {
|
|
26
|
+
await dropTableRls(pool, 'user_sign_in_countries');
|
|
27
|
+
await pool.query(sql`
|
|
28
|
+
drop table user_sign_in_countries;
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default alteration;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sql } from '@silverhand/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 sign_in_experiences
|
|
9
|
+
add column adaptive_mfa jsonb not null default '{}'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table sign_in_experiences drop column adaptive_mfa;
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default alteration;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable organization required MFA policy for the admin tenant.
|
|
9
|
+
* This allows tenant admins to require MFA for all tenant members
|
|
10
|
+
* by setting `isMfaRequired: true` on the organization.
|
|
11
|
+
*/
|
|
12
|
+
const alteration: AlterationScript = {
|
|
13
|
+
up: async (pool) => {
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
update sign_in_experiences
|
|
16
|
+
set mfa = jsonb_set(mfa, '{organizationRequiredMfaPolicy}', '"Mandatory"')
|
|
17
|
+
where tenant_id = ${adminTenantId}
|
|
18
|
+
and id = 'default'
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
down: async (pool) => {
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
update sign_in_experiences
|
|
24
|
+
set mfa = mfa - 'organizationRequiredMfaPolicy'
|
|
25
|
+
where tenant_id = ${adminTenantId}
|
|
26
|
+
and id = 'default'
|
|
27
|
+
`);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default alteration;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
/**
|
|
3
|
+
* Remove foreign key constraint from daily_active_users table to allow
|
|
4
|
+
* historical billing data to persist even after tenant deletion.
|
|
5
|
+
* This supports the MAU-based billing system requirements.
|
|
6
|
+
*
|
|
7
|
+
* Index optimizations:
|
|
8
|
+
* 1. Removes redundant (tenant_id, id) index (id is already primary key)
|
|
9
|
+
* 2. Adds optimized index (tenant_id, date, user_id) for aggregation queries
|
|
10
|
+
* 3. Replaces problematic partial index with BRIN index
|
|
11
|
+
*/
|
|
12
|
+
const alteration = {
|
|
13
|
+
up: async (pool) => {
|
|
14
|
+
// Drop the existing foreign key constraint
|
|
15
|
+
await pool.query(sql `
|
|
16
|
+
alter table daily_active_users
|
|
17
|
+
drop constraint if exists daily_active_users_tenant_id_fkey
|
|
18
|
+
`);
|
|
19
|
+
// Remove the redundant (tenant_id, id) index since id is already primary key
|
|
20
|
+
await pool.query(sql `
|
|
21
|
+
drop index if exists daily_active_users__id
|
|
22
|
+
`);
|
|
23
|
+
// Add optimized index for aggregation queries with better write performance
|
|
24
|
+
await pool.query(sql `
|
|
25
|
+
create index daily_active_users__tenant_date_user
|
|
26
|
+
on daily_active_users (tenant_id, date, user_id)
|
|
27
|
+
`);
|
|
28
|
+
// Add BRIN index for time-series date range queries
|
|
29
|
+
// Optimized for sequential data insertion and range scans (date >= ?)
|
|
30
|
+
await pool.query(sql `
|
|
31
|
+
create index daily_active_users__date_brin
|
|
32
|
+
on daily_active_users using brin (date)
|
|
33
|
+
`);
|
|
34
|
+
},
|
|
35
|
+
down: async (pool) => {
|
|
36
|
+
// Drop the new indexes we created
|
|
37
|
+
await pool.query(sql `
|
|
38
|
+
drop index if exists daily_active_users__date_brin
|
|
39
|
+
`);
|
|
40
|
+
await pool.query(sql `
|
|
41
|
+
drop index if exists daily_active_users__tenant_date_user
|
|
42
|
+
`);
|
|
43
|
+
// Recreate the original redundant index
|
|
44
|
+
await pool.query(sql `
|
|
45
|
+
create index daily_active_users__id
|
|
46
|
+
on daily_active_users (tenant_id, id)
|
|
47
|
+
`);
|
|
48
|
+
// Recreate the foreign key constraint
|
|
49
|
+
await pool.query(sql `
|
|
50
|
+
alter table daily_active_users
|
|
51
|
+
add constraint daily_active_users_tenant_id_fkey
|
|
52
|
+
foreign key (tenant_id) references tenants(id)
|
|
53
|
+
on update cascade on delete cascade
|
|
54
|
+
`);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export default alteration;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create aggregated_daily_active_users table for MAU-based billing system.
|
|
5
|
+
* This table consolidates daily user activities for efficient billing calculations.
|
|
6
|
+
*/
|
|
7
|
+
const alteration = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
// Create the aggregated daily active users table
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
create table aggregated_daily_active_users (
|
|
12
|
+
tenant_id varchar(21) not null,
|
|
13
|
+
activity_date date not null,
|
|
14
|
+
user_id varchar(21) not null,
|
|
15
|
+
activity_count integer not null,
|
|
16
|
+
primary key (tenant_id, activity_date, user_id)
|
|
17
|
+
);
|
|
18
|
+
`);
|
|
19
|
+
// Index for billing cycle range queries
|
|
20
|
+
await pool.query(sql `
|
|
21
|
+
create index aggregated_daily_active_users__tenant_date
|
|
22
|
+
on aggregated_daily_active_users (tenant_id, activity_date);
|
|
23
|
+
`);
|
|
24
|
+
// Index for tenant-specific user activity queries
|
|
25
|
+
await pool.query(sql `
|
|
26
|
+
create index aggregated_daily_active_users__tenant_user_date
|
|
27
|
+
on aggregated_daily_active_users (tenant_id, user_id, activity_date desc);
|
|
28
|
+
`);
|
|
29
|
+
await applyTableRls(pool, 'aggregated_daily_active_users');
|
|
30
|
+
},
|
|
31
|
+
down: async (pool) => {
|
|
32
|
+
// Drop RLS policies first
|
|
33
|
+
await dropTableRls(pool, 'aggregated_daily_active_users');
|
|
34
|
+
// Drop the table and all its indexes
|
|
35
|
+
await pool.query(sql `
|
|
36
|
+
drop table aggregated_daily_active_users;
|
|
37
|
+
`);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export default alteration;
|