@logto/schemas 1.40.1 → 1.42.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.41.0-1779864280-add-password-expiration-policy.ts +23 -0
- package/alterations/1.41.0-1779864281-add-is-password-expired-to-users.ts +18 -0
- package/alterations/1.41.0-1780358400-drop-oidc-model-instances-legacy-grant-id-index.ts +25 -0
- package/alterations/1.41.0-1780381219-add-username-policy.ts +41 -0
- package/alterations/1.41.0-1780643665-set-sign-up-profile-fields-default.ts +20 -0
- package/alterations/1.41.0-1780906060-add-verification-code-policy.ts +19 -0
- package/alterations/1.41.0-1781689400-add-sentinel-activities-created-at-index.ts +25 -0
- package/alterations/1.41.0-1782354362-set-admin-account-center-profile-fields.ts +28 -0
- package/alterations/1.41.0-1782375106-cover-service-logs-tenant-type-index-with-created-at.ts +36 -0
- package/alterations/1.42.0-1783916980-add-domain-verification-files.ts +20 -0
- package/alterations/1.42.0-1784619793-add-service-logs-created-at-index.ts +33 -0
- package/alterations/1.42.0-1784620000-add-saml-sso-connector-signing-keys-table.ts +38 -0
- package/alterations/utils/1704934999-tables.ts +22 -1
- package/alterations-js/1.41.0-1779864280-add-password-expiration-policy.js +19 -0
- package/alterations-js/1.41.0-1779864281-add-is-password-expired-to-users.js +14 -0
- package/alterations-js/1.41.0-1780358400-drop-oidc-model-instances-legacy-grant-id-index.js +21 -0
- package/alterations-js/1.41.0-1780381219-add-username-policy.js +37 -0
- package/alterations-js/1.41.0-1780643665-set-sign-up-profile-fields-default.js +16 -0
- package/alterations-js/1.41.0-1780906060-add-verification-code-policy.js +15 -0
- package/alterations-js/1.41.0-1781689400-add-sentinel-activities-created-at-index.js +21 -0
- package/alterations-js/1.41.0-1782354362-set-admin-account-center-profile-fields.js +23 -0
- package/alterations-js/1.41.0-1782375106-cover-service-logs-tenant-type-index-with-created-at.js +32 -0
- package/alterations-js/1.42.0-1783916980-add-domain-verification-files.js +16 -0
- package/alterations-js/1.42.0-1784619793-add-service-logs-created-at-index.js +29 -0
- package/alterations-js/1.42.0-1784620000-add-saml-sso-connector-signing-keys-table.js +33 -0
- package/alterations-js/utils/1704934999-tables.js +19 -1
- package/lib/consts/experience.d.ts +3 -0
- package/lib/consts/experience.js +3 -0
- package/lib/consts/index.d.ts +2 -0
- package/lib/consts/index.js +2 -0
- package/lib/consts/message-rate-limit.d.ts +65 -0
- package/lib/consts/message-rate-limit.js +29 -0
- package/lib/consts/message-rate-limit.test.d.ts +1 -0
- package/lib/consts/message-rate-limit.test.js +20 -0
- package/lib/consts/verification-code.d.ts +10 -0
- package/lib/consts/verification-code.js +10 -0
- package/lib/db-entries/domain.d.ts +4 -2
- package/lib/db-entries/domain.js +5 -1
- package/lib/db-entries/index.d.ts +1 -0
- package/lib/db-entries/index.js +1 -0
- package/lib/db-entries/saml-sso-connector-signing-key.d.ts +32 -0
- package/lib/db-entries/saml-sso-connector-signing-key.js +49 -0
- package/lib/db-entries/sign-in-experience.d.ts +10 -4
- package/lib/db-entries/sign-in-experience.js +13 -1
- package/lib/db-entries/user.d.ts +5 -1
- package/lib/db-entries/user.js +8 -0
- package/lib/foundations/jsonb-types/account-centers.d.ts +3 -0
- package/lib/foundations/jsonb-types/account-centers.js +1 -0
- package/lib/foundations/jsonb-types/custom-domain.d.ts +57 -0
- package/lib/foundations/jsonb-types/custom-domain.js +51 -0
- package/lib/foundations/jsonb-types/custom-domain.test.d.ts +1 -0
- package/lib/foundations/jsonb-types/custom-domain.test.js +38 -0
- package/lib/foundations/jsonb-types/hooks.d.ts +4 -4
- package/lib/foundations/jsonb-types/hooks.js +2 -0
- package/lib/foundations/jsonb-types/one-time-tokens.d.ts +5 -0
- package/lib/foundations/jsonb-types/one-time-tokens.js +2 -0
- package/lib/foundations/jsonb-types/sentinel.d.ts +16 -1
- package/lib/foundations/jsonb-types/sentinel.js +15 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +90 -3
- package/lib/foundations/jsonb-types/sign-in-experience.js +20 -0
- package/lib/foundations/jsonb-types/sign-in-experience.test.js +49 -1
- package/lib/foundations/jsonb-types/users.d.ts +9 -0
- package/lib/foundations/jsonb-types/users.js +1 -0
- package/lib/seeds/account-center.js +1 -0
- package/lib/seeds/sign-in-experience.js +1 -0
- package/lib/seeds/sign-in-experience.test.js +5 -1
- package/lib/types/application.d.ts +131 -1
- package/lib/types/application.js +8 -0
- package/lib/types/consent.d.ts +8 -0
- package/lib/types/custom-profile-fields.d.ts +5 -1
- package/lib/types/domain.d.ts +9 -0
- package/lib/types/hook.d.ts +9 -2
- package/lib/types/interaction-event.d.ts +11 -0
- package/lib/types/interaction-event.js +12 -0
- package/lib/types/interactions.d.ts +2 -11
- package/lib/types/interactions.js +5 -12
- package/lib/types/log/action.d.ts +7 -0
- package/lib/types/log/action.js +6 -0
- package/lib/types/log/index.d.ts +5 -2
- package/lib/types/log/index.js +1 -0
- package/lib/types/logto-config/action.d.ts +963 -0
- package/lib/types/logto-config/action.js +77 -0
- package/lib/types/logto-config/index.d.ts +91 -13
- package/lib/types/logto-config/index.js +12 -0
- package/lib/types/logto-config/index.test.js +25 -1
- package/lib/types/logto-config/jwt-customizer.d.ts +162 -1
- package/lib/types/logto-config/jwt-customizer.js +14 -0
- package/lib/types/organization.d.ts +136 -1
- package/lib/types/organization.js +3 -2
- package/lib/types/saml-application.d.ts +3 -0
- package/lib/types/saml-application.js +3 -0
- package/lib/types/sign-in-experience.d.ts +11 -4
- package/lib/types/sign-in-experience.js +1 -1
- package/lib/types/sso-connector.d.ts +58 -0
- package/lib/types/sso-connector.js +12 -0
- package/lib/types/ssr.d.ts +11 -0
- package/lib/types/user-assets.d.ts +10 -0
- package/lib/types/user-assets.js +17 -0
- package/lib/types/user-sessions.d.ts +309 -34
- package/lib/types/user-sessions.js +5 -0
- package/lib/types/user.d.ts +15 -0
- package/lib/types/user.js +1 -0
- package/lib/types/verification-records/one-time-token-verification.d.ts +5 -0
- package/package.json +8 -8
- package/tables/domains.sql +1 -0
- package/tables/oidc_model_instances.sql +0 -8
- package/tables/saml_sso_connector_signing_keys.sql +21 -0
- package/tables/sentinel_activities.sql +4 -0
- package/tables/service_logs.sql +7 -2
- package/tables/sign_in_experiences.sql +15 -2
- package/tables/users.sql +7 -0
|
@@ -0,0 +1,23 @@
|
|
|
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 users
|
|
9
|
+
add column password_updated_at timestamptz;
|
|
10
|
+
|
|
11
|
+
alter table sign_in_experiences
|
|
12
|
+
add column password_expiration jsonb not null default '{}'::jsonb;
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
down: async (pool) => {
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
alter table users drop column password_updated_at;
|
|
18
|
+
alter table sign_in_experiences drop column password_expiration;
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default alteration;
|
|
@@ -0,0 +1,18 @@
|
|
|
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 users add column is_password_expired boolean not null default false;
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
down: async (pool) => {
|
|
12
|
+
await pool.query(sql`
|
|
13
|
+
alter table users drop column is_password_expired;
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default alteration;
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
await pool.query(sql`
|
|
8
|
+
drop index concurrently if exists oidc_model_instances__model_name_payload_grant_id;
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
up: async () => {
|
|
12
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
13
|
+
},
|
|
14
|
+
beforeDown: async (pool) => {
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
create index concurrently if not exists oidc_model_instances__model_name_payload_grant_id
|
|
17
|
+
on oidc_model_instances (tenant_id, model_name, (payload->>'grantId'));
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async () => {
|
|
21
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default alteration;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
// `create index concurrently` cannot run in a transaction; the runner wraps up/down in one.
|
|
7
|
+
beforeUp: async (pool) => {
|
|
8
|
+
await pool.query(sql`
|
|
9
|
+
create index concurrently users__tenant_lower_username
|
|
10
|
+
on users (tenant_id, lower(username))
|
|
11
|
+
where username is not null;
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
up: async (pool) => {
|
|
15
|
+
// The default must be inlined: DDL cannot use bound parameters.
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
alter table sign_in_experiences
|
|
18
|
+
add column username_policy jsonb not null default '{
|
|
19
|
+
"caseSensitive": true,
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"maxLength": 128,
|
|
22
|
+
"allowedChars": {
|
|
23
|
+
"lowercase": true,
|
|
24
|
+
"uppercase": true,
|
|
25
|
+
"numbers": true,
|
|
26
|
+
"underscore": true
|
|
27
|
+
}
|
|
28
|
+
}'::jsonb;
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
beforeDown: async (pool) => {
|
|
32
|
+
await pool.query(sql`drop index concurrently users__tenant_lower_username;`);
|
|
33
|
+
},
|
|
34
|
+
down: async (pool) => {
|
|
35
|
+
await pool.query(sql`
|
|
36
|
+
alter table sign_in_experiences drop column username_policy;
|
|
37
|
+
`);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default alteration;
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
alter column sign_up_profile_fields set default '[]'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table sign_in_experiences
|
|
15
|
+
alter column sign_up_profile_fields drop default;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
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 if not exists verification_code_policy jsonb not null default '{}'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table sign_in_experiences drop column if exists verification_code_policy;
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default alteration;
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
await pool.query(sql`
|
|
8
|
+
create index concurrently if not exists sentinel_activities__created_at
|
|
9
|
+
on sentinel_activities (created_at);
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
up: async () => {
|
|
13
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
14
|
+
},
|
|
15
|
+
beforeDown: async (pool) => {
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
drop index concurrently if exists sentinel_activities__created_at;
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async () => {
|
|
21
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default alteration;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
update account_centers
|
|
11
|
+
set profile_fields = '[{"name": "name"}, {"name": "avatar"}]'::jsonb
|
|
12
|
+
where tenant_id = ${adminTenantId}
|
|
13
|
+
and id = 'default'
|
|
14
|
+
and profile_fields is null
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async (pool) => {
|
|
18
|
+
await pool.query(sql`
|
|
19
|
+
update account_centers
|
|
20
|
+
set profile_fields = null
|
|
21
|
+
where tenant_id = ${adminTenantId}
|
|
22
|
+
and id = 'default'
|
|
23
|
+
and profile_fields = '[{"name": "name"}, {"name": "avatar"}]'::jsonb
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default alteration;
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
// Cover the windowed hosted-email usage count (`where tenant_id=? and type=? and created_at>=?`) by
|
|
8
|
+
// adding `created_at` to the existing `(tenant_id, type)` index, then drop the now-redundant
|
|
9
|
+
// 2-column index (a left-prefix subset of the new one). `concurrently` keeps the write path
|
|
10
|
+
// unlocked — this unbounded table is written on every send.
|
|
11
|
+
await pool.query(sql`
|
|
12
|
+
create index concurrently if not exists service_logs__tenant_id__type__created_at
|
|
13
|
+
on service_logs (tenant_id, type, created_at);
|
|
14
|
+
`);
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
drop index concurrently if exists service_logs__tenant_id__type;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
up: async () => {
|
|
20
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
21
|
+
},
|
|
22
|
+
beforeDown: async (pool) => {
|
|
23
|
+
await pool.query(sql`
|
|
24
|
+
create index concurrently if not exists service_logs__tenant_id__type
|
|
25
|
+
on service_logs (tenant_id, type);
|
|
26
|
+
`);
|
|
27
|
+
await pool.query(sql`
|
|
28
|
+
drop index concurrently if exists service_logs__tenant_id__type__created_at;
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
down: async () => {
|
|
32
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default alteration;
|
|
@@ -0,0 +1,20 @@
|
|
|
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 domains
|
|
9
|
+
add column verification_files jsonb not null default '[]'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table domains
|
|
15
|
+
drop column verification_files;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
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
|
+
const alteration: AlterationScript = {
|
|
6
|
+
beforeUp: async (pool) => {
|
|
7
|
+
// Support the age-based `service_logs` retention prune's global oldest-first scan
|
|
8
|
+
// (`where created_at < ? order by created_at asc limit ?`). The existing
|
|
9
|
+
// `(tenant_id, type, created_at)` index leads with `tenant_id`, so it can't drive a
|
|
10
|
+
// tenant-agnostic time-range scan. `concurrently` keeps the write path unlocked — this
|
|
11
|
+
// unbounded table is written on every send.
|
|
12
|
+
//
|
|
13
|
+
// No `if not exists`: fail loud if the index already exists (e.g. a leftover invalid build from a
|
|
14
|
+
// prior failed concurrent creation) instead of silently skipping.
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
create index concurrently service_logs__created_at
|
|
17
|
+
on service_logs (created_at);
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
up: async () => {
|
|
21
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
22
|
+
},
|
|
23
|
+
beforeDown: async (pool) => {
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
drop index concurrently if exists service_logs__created_at;
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
down: async () => {
|
|
29
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default alteration;
|
|
@@ -0,0 +1,38 @@
|
|
|
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 saml_sso_connector_signing_keys (
|
|
11
|
+
id varchar(21) not null,
|
|
12
|
+
tenant_id varchar(21) not null
|
|
13
|
+
references tenants (id) on update cascade on delete cascade,
|
|
14
|
+
sso_connector_id varchar(128) not null
|
|
15
|
+
references sso_connectors (id) on update cascade on delete cascade,
|
|
16
|
+
private_key text not null,
|
|
17
|
+
certificate text not null,
|
|
18
|
+
created_at timestamptz not null default now(),
|
|
19
|
+
expires_at timestamptz not null,
|
|
20
|
+
active boolean not null,
|
|
21
|
+
primary key (tenant_id, sso_connector_id, id)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
create unique index saml_sso_connector_signing_keys__unique_active
|
|
25
|
+
on saml_sso_connector_signing_keys (tenant_id, sso_connector_id, active)
|
|
26
|
+
where active;
|
|
27
|
+
`);
|
|
28
|
+
await applyTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
29
|
+
},
|
|
30
|
+
down: async (pool) => {
|
|
31
|
+
await dropTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
32
|
+
await pool.query(sql`
|
|
33
|
+
drop table saml_sso_connector_signing_keys;
|
|
34
|
+
`);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default alteration;
|
|
@@ -2,12 +2,33 @@ import { type CommonQueryMethods, sql } from '@silverhand/slonik';
|
|
|
2
2
|
|
|
3
3
|
const getId = (value: string) => sql.identifier([value]);
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Returns the suffix used in the base Logto role name for the current database
|
|
7
|
+
* (`logto_tenant_<suffix>`).
|
|
8
|
+
*
|
|
9
|
+
* Historically the suffix was the raw database name (which may contain hyphens). A later
|
|
10
|
+
* convention normalised it by replacing hyphens with underscores. To remain compatible with
|
|
11
|
+
* both conventions we probe `pg_roles` for the normalised name first and fall back to the
|
|
12
|
+
* raw database name for legacy installations.
|
|
13
|
+
*/
|
|
5
14
|
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
6
15
|
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
7
16
|
select current_database();
|
|
8
17
|
`);
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
const normalized = currentDatabase.replaceAll('-', '_');
|
|
20
|
+
|
|
21
|
+
if (normalized === currentDatabase) {
|
|
22
|
+
return currentDatabase;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { exists } = await pool.one<{ exists: boolean }>(sql`
|
|
26
|
+
select exists(
|
|
27
|
+
select 1 from pg_roles where rolname = ${'logto_tenant_' + normalized}
|
|
28
|
+
) as exists;
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
return exists ? normalized : currentDatabase;
|
|
11
32
|
};
|
|
12
33
|
|
|
13
34
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table users
|
|
6
|
+
add column password_updated_at timestamptz;
|
|
7
|
+
|
|
8
|
+
alter table sign_in_experiences
|
|
9
|
+
add column password_expiration jsonb not null default '{}'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql `
|
|
14
|
+
alter table users drop column password_updated_at;
|
|
15
|
+
alter table sign_in_experiences drop column password_expiration;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export default alteration;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table users add column is_password_expired boolean not null default false;
|
|
6
|
+
`);
|
|
7
|
+
},
|
|
8
|
+
down: async (pool) => {
|
|
9
|
+
await pool.query(sql `
|
|
10
|
+
alter table users drop column is_password_expired;
|
|
11
|
+
`);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
drop index concurrently if exists oidc_model_instances__model_name_payload_grant_id;
|
|
6
|
+
`);
|
|
7
|
+
},
|
|
8
|
+
up: async () => {
|
|
9
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
10
|
+
},
|
|
11
|
+
beforeDown: async (pool) => {
|
|
12
|
+
await pool.query(sql `
|
|
13
|
+
create index concurrently if not exists oidc_model_instances__model_name_payload_grant_id
|
|
14
|
+
on oidc_model_instances (tenant_id, model_name, (payload->>'grantId'));
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async () => {
|
|
18
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
// `create index concurrently` cannot run in a transaction; the runner wraps up/down in one.
|
|
4
|
+
beforeUp: async (pool) => {
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
create index concurrently users__tenant_lower_username
|
|
7
|
+
on users (tenant_id, lower(username))
|
|
8
|
+
where username is not null;
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
up: async (pool) => {
|
|
12
|
+
// The default must be inlined: DDL cannot use bound parameters.
|
|
13
|
+
await pool.query(sql `
|
|
14
|
+
alter table sign_in_experiences
|
|
15
|
+
add column username_policy jsonb not null default '{
|
|
16
|
+
"caseSensitive": true,
|
|
17
|
+
"minLength": 1,
|
|
18
|
+
"maxLength": 128,
|
|
19
|
+
"allowedChars": {
|
|
20
|
+
"lowercase": true,
|
|
21
|
+
"uppercase": true,
|
|
22
|
+
"numbers": true,
|
|
23
|
+
"underscore": true
|
|
24
|
+
}
|
|
25
|
+
}'::jsonb;
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
beforeDown: async (pool) => {
|
|
29
|
+
await pool.query(sql `drop index concurrently users__tenant_lower_username;`);
|
|
30
|
+
},
|
|
31
|
+
down: async (pool) => {
|
|
32
|
+
await pool.query(sql `
|
|
33
|
+
alter table sign_in_experiences drop column username_policy;
|
|
34
|
+
`);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export default alteration;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table sign_in_experiences
|
|
6
|
+
alter column sign_up_profile_fields set default '[]'::jsonb;
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table sign_in_experiences
|
|
12
|
+
alter column sign_up_profile_fields drop default;
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export default alteration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table sign_in_experiences
|
|
6
|
+
add column if not exists verification_code_policy jsonb not null default '{}'::jsonb;
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table sign_in_experiences drop column if exists verification_code_policy;
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
create index concurrently if not exists sentinel_activities__created_at
|
|
6
|
+
on sentinel_activities (created_at);
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
up: async () => {
|
|
10
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
11
|
+
},
|
|
12
|
+
beforeDown: async (pool) => {
|
|
13
|
+
await pool.query(sql `
|
|
14
|
+
drop index concurrently if exists sentinel_activities__created_at;
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async () => {
|
|
18
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const adminTenantId = 'admin';
|
|
3
|
+
const alteration = {
|
|
4
|
+
up: async (pool) => {
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
update account_centers
|
|
7
|
+
set profile_fields = '[{"name": "name"}, {"name": "avatar"}]'::jsonb
|
|
8
|
+
where tenant_id = ${adminTenantId}
|
|
9
|
+
and id = 'default'
|
|
10
|
+
and profile_fields is null
|
|
11
|
+
`);
|
|
12
|
+
},
|
|
13
|
+
down: async (pool) => {
|
|
14
|
+
await pool.query(sql `
|
|
15
|
+
update account_centers
|
|
16
|
+
set profile_fields = null
|
|
17
|
+
where tenant_id = ${adminTenantId}
|
|
18
|
+
and id = 'default'
|
|
19
|
+
and profile_fields = '[{"name": "name"}, {"name": "avatar"}]'::jsonb
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export default alteration;
|
package/alterations-js/1.41.0-1782375106-cover-service-logs-tenant-type-index-with-created-at.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
// Cover the windowed hosted-email usage count (`where tenant_id=? and type=? and created_at>=?`) by
|
|
5
|
+
// adding `created_at` to the existing `(tenant_id, type)` index, then drop the now-redundant
|
|
6
|
+
// 2-column index (a left-prefix subset of the new one). `concurrently` keeps the write path
|
|
7
|
+
// unlocked — this unbounded table is written on every send.
|
|
8
|
+
await pool.query(sql `
|
|
9
|
+
create index concurrently if not exists service_logs__tenant_id__type__created_at
|
|
10
|
+
on service_logs (tenant_id, type, created_at);
|
|
11
|
+
`);
|
|
12
|
+
await pool.query(sql `
|
|
13
|
+
drop index concurrently if exists service_logs__tenant_id__type;
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
up: async () => {
|
|
17
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
18
|
+
},
|
|
19
|
+
beforeDown: async (pool) => {
|
|
20
|
+
await pool.query(sql `
|
|
21
|
+
create index concurrently if not exists service_logs__tenant_id__type
|
|
22
|
+
on service_logs (tenant_id, type);
|
|
23
|
+
`);
|
|
24
|
+
await pool.query(sql `
|
|
25
|
+
drop index concurrently if exists service_logs__tenant_id__type__created_at;
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
down: async () => {
|
|
29
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table domains
|
|
6
|
+
add column verification_files jsonb not null default '[]'::jsonb;
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table domains
|
|
12
|
+
drop column verification_files;
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export default alteration;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
// Support the age-based `service_logs` retention prune's global oldest-first scan
|
|
5
|
+
// (`where created_at < ? order by created_at asc limit ?`). The existing
|
|
6
|
+
// `(tenant_id, type, created_at)` index leads with `tenant_id`, so it can't drive a
|
|
7
|
+
// tenant-agnostic time-range scan. `concurrently` keeps the write path unlocked — this
|
|
8
|
+
// unbounded table is written on every send.
|
|
9
|
+
//
|
|
10
|
+
// No `if not exists`: fail loud if the index already exists (e.g. a leftover invalid build from a
|
|
11
|
+
// prior failed concurrent creation) instead of silently skipping.
|
|
12
|
+
await pool.query(sql `
|
|
13
|
+
create index concurrently service_logs__created_at
|
|
14
|
+
on service_logs (created_at);
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
up: async () => {
|
|
18
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
19
|
+
},
|
|
20
|
+
beforeDown: async (pool) => {
|
|
21
|
+
await pool.query(sql `
|
|
22
|
+
drop index concurrently if exists service_logs__created_at;
|
|
23
|
+
`);
|
|
24
|
+
},
|
|
25
|
+
down: async () => {
|
|
26
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export default alteration;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
const alteration = {
|
|
4
|
+
up: async (pool) => {
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
create table saml_sso_connector_signing_keys (
|
|
7
|
+
id varchar(21) not null,
|
|
8
|
+
tenant_id varchar(21) not null
|
|
9
|
+
references tenants (id) on update cascade on delete cascade,
|
|
10
|
+
sso_connector_id varchar(128) not null
|
|
11
|
+
references sso_connectors (id) on update cascade on delete cascade,
|
|
12
|
+
private_key text not null,
|
|
13
|
+
certificate text not null,
|
|
14
|
+
created_at timestamptz not null default now(),
|
|
15
|
+
expires_at timestamptz not null,
|
|
16
|
+
active boolean not null,
|
|
17
|
+
primary key (tenant_id, sso_connector_id, id)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
create unique index saml_sso_connector_signing_keys__unique_active
|
|
21
|
+
on saml_sso_connector_signing_keys (tenant_id, sso_connector_id, active)
|
|
22
|
+
where active;
|
|
23
|
+
`);
|
|
24
|
+
await applyTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
25
|
+
},
|
|
26
|
+
down: async (pool) => {
|
|
27
|
+
await dropTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
28
|
+
await pool.query(sql `
|
|
29
|
+
drop table saml_sso_connector_signing_keys;
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default alteration;
|