mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(schemas): init tenant sqls
This commit is contained in:
parent
53f49df621
commit
ba44eb5fc6
32 changed files with 362 additions and 93 deletions
|
@ -13,7 +13,7 @@ import {
|
|||
managementResourceScope,
|
||||
defaultRoleScopeRelation,
|
||||
} from '@logto/schemas';
|
||||
import { Hooks } from '@logto/schemas/models';
|
||||
import { Hooks, Tenants } from '@logto/schemas/models';
|
||||
import chalk from 'chalk';
|
||||
import type { DatabasePool, DatabaseTransactionConnection } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
|
@ -33,6 +33,31 @@ import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
|||
import { getAlterationDirectory } from '../alteration/utils.js';
|
||||
import { oidcConfigReaders } from './oidc-config.js';
|
||||
|
||||
const getExplicitOrder = (query: string) => {
|
||||
const matched = /\/\*\s*init_order\s*=\s*(\d+)\s*\*\//.exec(query)?.[1];
|
||||
|
||||
return matched ? Number(matched) : undefined;
|
||||
};
|
||||
|
||||
const compareQuery = ([t1, q1]: [string, string], [t2, q2]: [string, string]) => {
|
||||
const o1 = getExplicitOrder(q1);
|
||||
const o2 = getExplicitOrder(q2);
|
||||
|
||||
if (o1 === undefined && o2 === undefined) {
|
||||
return t1.localeCompare(t2);
|
||||
}
|
||||
|
||||
if (o1 === undefined) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o2 === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return o1 - o2;
|
||||
};
|
||||
|
||||
const createTables = async (connection: DatabaseTransactionConnection) => {
|
||||
const tableDirectory = getPathInModule('@logto/schemas', 'tables');
|
||||
const directoryFiles = await readdir(tableDirectory);
|
||||
|
@ -44,16 +69,19 @@ const createTables = async (connection: DatabaseTransactionConnection) => {
|
|||
])
|
||||
);
|
||||
|
||||
// Await in loop is intended for better error handling
|
||||
for (const [, query] of queries) {
|
||||
console.log(Tenants.raw, getExplicitOrder(Tenants.raw));
|
||||
|
||||
const allQueries: Array<[string, string]> = [
|
||||
[Hooks.tableName, Hooks.raw],
|
||||
[Tenants.tableName, Tenants.raw],
|
||||
...queries,
|
||||
];
|
||||
const sorted = allQueries.slice().sort(compareQuery);
|
||||
|
||||
for (const [, query] of sorted) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await connection.query(sql`${raw(query)}`);
|
||||
}
|
||||
|
||||
for (const table of [Hooks]) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await connection.query(sql`${raw(table.raw)}`);
|
||||
}
|
||||
};
|
||||
|
||||
const seedTables = async (connection: DatabaseTransactionConnection, latestTimestamp: number) => {
|
||||
|
@ -133,6 +161,7 @@ export const seedByPool = async (pool: DatabasePool, type: SeedChoice) => {
|
|||
);
|
||||
}
|
||||
|
||||
await createTables(connection);
|
||||
await oraPromise(createTables(connection), {
|
||||
text: 'Create tables',
|
||||
prefixText: chalk.blue('[info]'),
|
||||
|
|
140
packages/schemas/alterations/next-1674032099-multi-tenancy.ts
Normal file
140
packages/schemas/alterations/next-1674032099-multi-tenancy.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
import { conditionalString } from '@silverhand/essentials';
|
||||
import { sql } from 'slonik';
|
||||
import { raw } from 'slonik-sql-tag-raw';
|
||||
|
||||
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||
|
||||
const getId = (value: string) => sql.identifier([value]);
|
||||
const tenantId = sql.identifier(['tenant_id']);
|
||||
const defaultTenantId = 'default';
|
||||
|
||||
// [table name, primary key array]
|
||||
type TableInfo = [string, string[]];
|
||||
|
||||
const tables: TableInfo[] = [
|
||||
['applications', ['id']],
|
||||
['connectors', ['id']],
|
||||
['custom_phrases', ['language_tag']],
|
||||
['logs', ['id']],
|
||||
['oidc_model_instances', ['model_name', 'id']],
|
||||
['passcodes', ['id']],
|
||||
['resources', ['id']],
|
||||
['roles', ['id']],
|
||||
['roles_scopes', ['role_id', 'scope_id']],
|
||||
['scopes', ['id']],
|
||||
['settings', ['id']],
|
||||
['sign_in_experiences', ['id']],
|
||||
['users_roles', ['user_id', 'role_id']],
|
||||
['users', ['id']],
|
||||
];
|
||||
|
||||
type IndexInfo = {
|
||||
table: string;
|
||||
indexes: Array<{ name?: string; type?: 'unique'; columns: string[]; strategy?: 'drop-only' }>;
|
||||
};
|
||||
|
||||
const indexes: IndexInfo[] = [
|
||||
{
|
||||
table: 'logs',
|
||||
indexes: [
|
||||
{ columns: ['key'] },
|
||||
{ columns: ['created_at'], strategy: 'drop-only' },
|
||||
{ name: 'user_id', columns: ["(payload->>'user_id') nulls last"] },
|
||||
{ name: 'application_id', columns: ["(payload->>'application_id') nulls last"] },
|
||||
],
|
||||
},
|
||||
{
|
||||
table: 'oidc_model_instances',
|
||||
indexes: [
|
||||
{ name: 'model_name_payload_user_code', columns: ['model_name', "(payload->>'userCode')"] },
|
||||
{ name: 'model_name_payload_uid', columns: ['model_name', "(payload->>'uid')"] },
|
||||
{ name: 'model_name_payload_grant_id', columns: ['model_name', "(payload->>'grantId')"] },
|
||||
],
|
||||
},
|
||||
{
|
||||
table: 'passcodes',
|
||||
indexes: [
|
||||
{ columns: ['interaction_jti', 'type'] },
|
||||
{ columns: ['email', 'type'] },
|
||||
{ columns: ['phone', 'type'] },
|
||||
],
|
||||
},
|
||||
{ table: 'resources', indexes: [{ type: 'unique', columns: ['indicator'] }] },
|
||||
{ table: 'roles', indexes: [{ type: 'unique', columns: ['name'] }] },
|
||||
{ table: 'scopes', indexes: [{ type: 'unique', columns: ['resource_id', 'name'] }] },
|
||||
{
|
||||
table: 'users',
|
||||
indexes: [{ columns: ['name'] }, { columns: ['created_at'], strategy: 'drop-only' }],
|
||||
},
|
||||
];
|
||||
|
||||
const alteration: AlterationScript = {
|
||||
up: async (pool) => {
|
||||
await pool.query(sql`
|
||||
create table tenants (
|
||||
id varchar(21) not null,
|
||||
db_user_password varchar(128),
|
||||
primary key (id)
|
||||
);
|
||||
`);
|
||||
|
||||
await pool.query(sql`
|
||||
insert into tenants (${getId('id')}, ${getId('db_user_password')})
|
||||
values (${defaultTenantId}, null);
|
||||
`);
|
||||
|
||||
// Update primary keys
|
||||
await Promise.all(
|
||||
tables.map(async ([tableName, primaryKeys]) => {
|
||||
// Add `tenant_id` column and set existing data to a default tenant
|
||||
await pool.query(sql`
|
||||
alter table ${sql.identifier([tableName])}
|
||||
add column ${tenantId} varchar(21) not null default 'default'
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
drop constraint ${sql.identifier([tableName + '_pkey'])} cascade,
|
||||
add primary key (${sql.join(
|
||||
['tenant_id', ...primaryKeys].map((key) => sql.identifier([key])),
|
||||
sql`, `
|
||||
)});
|
||||
`);
|
||||
|
||||
// Column should not have a default tenant ID, it should be always manually assigned
|
||||
await pool.query(sql`
|
||||
alter table ${sql.identifier([tableName])}
|
||||
alter column ${tenantId} drop default;
|
||||
`);
|
||||
})
|
||||
);
|
||||
|
||||
// Update indexes
|
||||
await Promise.all(
|
||||
indexes.flatMap(({ table, indexes }) =>
|
||||
indexes.map(async ({ name, type, columns, strategy }) => {
|
||||
const indexName = getId(`${table}__${name ?? columns.join('_')}`);
|
||||
await pool.query(sql`drop index ${indexName}`);
|
||||
|
||||
if (strategy !== 'drop-only') {
|
||||
await pool.query(
|
||||
sql`
|
||||
create ${raw(conditionalString(type))} index ${indexName}
|
||||
on ${getId(table)}
|
||||
(
|
||||
${tenantId},
|
||||
${sql.join(
|
||||
columns.map((column) => raw(column)),
|
||||
sql`, `
|
||||
)}
|
||||
);
|
||||
`
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
down: async (pool) => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -52,7 +52,9 @@
|
|||
"lint-staged": "^13.0.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"prettier": "^2.8.1",
|
||||
"roarr": "^7.11.0",
|
||||
"slonik": "^30.0.0",
|
||||
"slonik-sql-tag-raw": "^1.1.4",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
|
|
@ -44,8 +44,8 @@ const generate = async () => {
|
|||
// Get statements
|
||||
const statements = paragraph
|
||||
.split(';')
|
||||
.map((value) => normalizeWhitespaces(value))
|
||||
.map((value) => removeUnrecognizedComments(value));
|
||||
.map((value) => removeUnrecognizedComments(value))
|
||||
.map((value) => normalizeWhitespaces(value));
|
||||
|
||||
// Parse Table statements
|
||||
const tables = statements
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createModel } from '@withtyped/server';
|
||||
|
||||
export const Tenants = createModel(/* sql */ `
|
||||
/* init_order = 0 */
|
||||
create table tenants (
|
||||
id varchar(32) not null,
|
||||
db_user_password varchar(128) not null,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { CreateApplication } from '../db-entries/index.js';
|
||||
import { ApplicationType } from '../db-entries/index.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
/**
|
||||
* The fixed application ID for Admin Console.
|
||||
|
@ -11,6 +12,7 @@ export const adminConsoleApplicationId = 'admin-console';
|
|||
export const demoAppApplicationId = 'demo-app';
|
||||
|
||||
export const createDemoAppApplication = (secret: string): Readonly<CreateApplication> => ({
|
||||
tenantId: defaultTenantId,
|
||||
id: demoAppApplicationId,
|
||||
secret,
|
||||
name: 'Demo App',
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import type { CreateResource } from '../db-entries/index.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
export const managementResourceId = 'management-api';
|
||||
|
||||
export const managementResource: Readonly<CreateResource> = Object.freeze({
|
||||
tenantId: defaultTenantId,
|
||||
id: managementResourceId,
|
||||
/**
|
||||
* The fixed resource indicator for Management APIs.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { CreateRole, CreateRolesScope } from '../db-entries/index.js';
|
||||
import { UserRole } from '../types/index.js';
|
||||
import { managementResourceScopeId } from './scope.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
export const adminConsoleAdminRoleId = 'ac-admin-id';
|
||||
|
||||
|
@ -8,12 +9,14 @@ export const adminConsoleAdminRoleId = 'ac-admin-id';
|
|||
* Default Admin Role for Admin Console.
|
||||
*/
|
||||
export const defaultRole: Readonly<CreateRole> = {
|
||||
tenantId: defaultTenantId,
|
||||
id: adminConsoleAdminRoleId,
|
||||
name: UserRole.Admin,
|
||||
description: 'Admin role for Logto.',
|
||||
};
|
||||
|
||||
export const defaultRoleScopeRelation: Readonly<CreateRolesScope> = {
|
||||
tenantId: defaultTenantId,
|
||||
roleId: adminConsoleAdminRoleId,
|
||||
scopeId: managementResourceScopeId,
|
||||
};
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import type { CreateScope } from '../db-entries/index.js';
|
||||
import { managementResourceId } from './resource.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
export const managementResourceScopeId = 'management-api-scope';
|
||||
|
||||
export const managementResourceScope: Readonly<CreateScope> = Object.freeze({
|
||||
tenantId: defaultTenantId,
|
||||
id: managementResourceScopeId,
|
||||
name: 'management-api:default',
|
||||
description: 'Default scope for management API',
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import type { CreateSetting } from '../db-entries/index.js';
|
||||
import { AppearanceMode } from '../foundations/index.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
export const defaultSettingId = 'default';
|
||||
|
||||
export const createDefaultSetting = (): Readonly<CreateSetting> =>
|
||||
Object.freeze({
|
||||
tenantId: defaultTenantId,
|
||||
id: defaultSettingId,
|
||||
adminConsole: {
|
||||
language: 'en',
|
||||
|
|
|
@ -3,10 +3,12 @@ import { generateDarkColor } from '@logto/core-kit';
|
|||
import type { CreateSignInExperience } from '../db-entries/index.js';
|
||||
import { SignInMode } from '../db-entries/index.js';
|
||||
import { BrandingStyle, SignInIdentifier } from '../foundations/index.js';
|
||||
import { defaultTenantId } from './tenant.js';
|
||||
|
||||
const defaultPrimaryColor = '#6139F6';
|
||||
|
||||
export const defaultSignInExperience: Readonly<CreateSignInExperience> = {
|
||||
tenantId: defaultTenantId,
|
||||
id: 'default',
|
||||
color: {
|
||||
primaryColor: defaultPrimaryColor,
|
||||
|
@ -52,5 +54,6 @@ export const adminConsoleSignInExperience: CreateSignInExperience = {
|
|||
style: BrandingStyle.Logo_Slogan,
|
||||
logoUrl: 'https://logto.io/logo.svg',
|
||||
darkLogoUrl: 'https://logto.io/logo-dark.svg',
|
||||
slogan: 'admin_console.welcome.title', // TODO: @simeng should we programmatically support an i18n key for slogan?
|
||||
},
|
||||
};
|
||||
|
|
2
packages/schemas/src/seeds/tenant.ts
Normal file
2
packages/schemas/src/seeds/tenant.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const defaultTenantId = 'default';
|
||||
export const adminTenantId = 'admin';
|
|
@ -1,6 +1,10 @@
|
|||
/* init_order = 1 */
|
||||
|
||||
create type application_type as enum ('Native', 'SPA', 'Traditional', 'MachineToMachine');
|
||||
|
||||
create table applications (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
name varchar(256) not null,
|
||||
secret varchar(64) not null,
|
||||
|
@ -11,3 +15,6 @@ create table applications (
|
|||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index applications__id
|
||||
on applications (tenant_id, id)
|
||||
|
|
15
packages/schemas/tables/applications_roles.sql
Normal file
15
packages/schemas/tables/applications_roles.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
create table applications_roles (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
application_id varchar(21) not null
|
||||
references applications (id) on update cascade on delete cascade,
|
||||
role_id varchar(21) not null
|
||||
references roles (id) on update cascade on delete cascade,
|
||||
primary key (id),
|
||||
constraint applications_roles__application_id_role_id
|
||||
unique (tenant_id, application_id, role_id)
|
||||
);
|
||||
|
||||
create index applications_roles__id
|
||||
on applications_roles (tenant_id, id);
|
|
@ -1,4 +1,6 @@
|
|||
create table connectors (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(128) not null,
|
||||
sync_profile boolean not null default FALSE,
|
||||
connector_id varchar(128) not null,
|
||||
|
@ -7,3 +9,6 @@ create table connectors (
|
|||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index connectors__id
|
||||
on connectors (tenant_id, id);
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
create table custom_phrases (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
language_tag varchar(16) not null,
|
||||
translation jsonb /* @use Translation */ not null,
|
||||
primary key(language_tag)
|
||||
primary key (id),
|
||||
constraint custom_phrases__language_tag
|
||||
unique (tenant_id, language_tag)
|
||||
);
|
||||
|
||||
create index custom_phrases__id
|
||||
on custom_phrases (tenant_id, id);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
create table logs
|
||||
(
|
||||
create table logs (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
key varchar(128) not null,
|
||||
payload jsonb /* @use LogContextPayload */ not null default '{}'::jsonb,
|
||||
|
@ -7,7 +8,14 @@ create table logs
|
|||
primary key (id)
|
||||
);
|
||||
|
||||
create index logs__key on logs (key);
|
||||
create index logs__created_at on logs (created_at);
|
||||
create index logs__user_id on logs ((payload->>'user_id') nulls last);
|
||||
create index logs__application_id on logs ((payload->>'application_id') nulls last);
|
||||
create index logs__id
|
||||
on logs (tenant_id, id);
|
||||
|
||||
create index logs__key
|
||||
on logs (tenant_id, key);
|
||||
|
||||
create index logs__user_id
|
||||
on logs (tenant_id, (payload->>'user_id') nulls last);
|
||||
|
||||
create index logs__application_id
|
||||
on logs (tenant_id, (payload->>'application_id') nulls last);
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
create table oidc_model_instances (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
model_name varchar(64) not null,
|
||||
id varchar(128) not null,
|
||||
payload jsonb /* @use OidcModelInstancePayload */ not null,
|
||||
expires_at timestamptz not null,
|
||||
consumed_at timestamptz,
|
||||
primary key (model_name, id)
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_id
|
||||
on oidc_model_instances (tenant_id, model_name, id);
|
||||
|
||||
create index oidc_model_instances__model_name_payload_user_code
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
(payload->>'userCode')
|
||||
);
|
||||
on oidc_model_instances (
|
||||
tenant_id,
|
||||
model_name,
|
||||
(payload->>'userCode')
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_payload_uid
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
(payload->>'uid')
|
||||
);
|
||||
on oidc_model_instances (
|
||||
tenant_id,
|
||||
model_name,
|
||||
(payload->>'uid')
|
||||
);
|
||||
|
||||
create index oidc_model_instances__model_name_payload_grant_id
|
||||
on oidc_model_instances (
|
||||
model_name,
|
||||
(payload->>'grantId')
|
||||
);
|
||||
on oidc_model_instances (
|
||||
tenant_id,
|
||||
model_name,
|
||||
(payload->>'grantId')
|
||||
);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
create table passcodes (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
interaction_jti varchar(128),
|
||||
phone varchar(32),
|
||||
|
@ -11,20 +13,14 @@ create table passcodes (
|
|||
primary key (id)
|
||||
);
|
||||
|
||||
create index passcodes__id
|
||||
on passcodes (tenant_id, id);
|
||||
|
||||
create index passcodes__interaction_jti_type
|
||||
on passcodes (
|
||||
interaction_jti,
|
||||
type
|
||||
);
|
||||
on passcodes (tenant_id, interaction_jti, type);
|
||||
|
||||
create index passcodes__email_type
|
||||
on passcodes (
|
||||
email,
|
||||
type
|
||||
);
|
||||
on passcodes (tenant_id, email, type);
|
||||
|
||||
create index passcodes__phone_type
|
||||
on passcodes (
|
||||
phone,
|
||||
type
|
||||
);
|
||||
on passcodes (tenant_id, phone, type);
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
create table resources (
|
||||
id varchar(21) not null,
|
||||
name text not null,
|
||||
indicator text not null unique, /* resource indicator also used as audience */
|
||||
access_token_ttl bigint not null default(3600), /* expiration value in seconds, default is 1h */
|
||||
primary key (id)
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
name text not null,
|
||||
indicator text not null unique, /* resource indicator also used as audience */
|
||||
access_token_ttl bigint not null default(3600), /* expiration value in seconds, default is 1h */
|
||||
primary key (id),
|
||||
constraint resources__indicator
|
||||
unique (tenant_id, indicator)
|
||||
);
|
||||
|
||||
create unique index resources__indicator
|
||||
on resources (
|
||||
indicator
|
||||
);
|
||||
create index resources__id
|
||||
on resources (tenant_id, indicator);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
/* init_order = 1 */
|
||||
|
||||
create table roles (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
name varchar(128) not null,
|
||||
description varchar(128) not null,
|
||||
primary key (id)
|
||||
primary key (id),
|
||||
constraint roles__name
|
||||
unique (tenant_id, name)
|
||||
);
|
||||
|
||||
create unique index roles__name
|
||||
on roles (
|
||||
name
|
||||
);
|
||||
create index roles__id
|
||||
on roles (tenant_id, id);
|
||||
|
|
15
packages/schemas/tables/roles_scopes.sql
Normal file
15
packages/schemas/tables/roles_scopes.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
create table roles_scopes (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
role_id varchar(21) not null
|
||||
references roles (id) on update cascade on delete cascade,
|
||||
scope_id varchar(21) not null
|
||||
references scopes (id) on update cascade on delete cascade,
|
||||
primary key (id),
|
||||
constraint roles_scopes__role_id_scope_id
|
||||
unique (tenant_id, role_id, scope_id)
|
||||
);
|
||||
|
||||
create index roles_scopes__id
|
||||
on roles_scopes (tenant_id, id);
|
|
@ -1,5 +0,0 @@
|
|||
create table applications_roles (
|
||||
application_id varchar(21) not null references applications (id) on update cascade on delete cascade,
|
||||
role_id varchar(21) not null references roles (id) on update cascade on delete cascade,
|
||||
primary key (application_id, role_id)
|
||||
);
|
|
@ -1,14 +1,16 @@
|
|||
create table scopes (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
resource_id varchar(21) not null references resources (id) on update cascade on delete cascade,
|
||||
resource_id varchar(21) not null
|
||||
references resources (id) on update cascade on delete cascade,
|
||||
name varchar(256) not null,
|
||||
description text not null,
|
||||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
primary key (id),
|
||||
constraint scopes__resource_id_name
|
||||
unique (tenant_id, resource_id, name)
|
||||
);
|
||||
|
||||
create index scopes__resource_id_name
|
||||
on scopes (
|
||||
resource_id,
|
||||
name
|
||||
);
|
||||
create index scopes__id
|
||||
on scopes (tenant_id, id);
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
create table roles_scopes (
|
||||
role_id varchar(21) not null references roles (id) on update cascade on delete cascade,
|
||||
scope_id varchar(21) not null references scopes (id) on update cascade on delete cascade,
|
||||
primary key (role_id, scope_id)
|
||||
);
|
|
@ -1,5 +1,10 @@
|
|||
create table settings (
|
||||
id varchar(21) not null,
|
||||
admin_console jsonb /* @use AdminConsoleConfig */ not null,
|
||||
primary key (id)
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
admin_console jsonb /* @use AdminConsoleConfig */ not null,
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index settings__id
|
||||
on settings (tenant_id, id);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
create type sign_in_mode as enum ('SignIn', 'Register', 'SignInAndRegister');
|
||||
|
||||
create table sign_in_experiences (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(21) not null,
|
||||
color jsonb /* @use Color */ not null,
|
||||
branding jsonb /* @use Branding */ not null,
|
||||
|
@ -12,3 +14,6 @@ create table sign_in_experiences (
|
|||
sign_in_mode sign_in_mode not null default 'SignInAndRegister',
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index sign_in_experiences__id
|
||||
on sign_in_experiences (tenant_id, id);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/* init_order = 1 */
|
||||
|
||||
create type users_password_encryption_method as enum ('Argon2i');
|
||||
|
||||
create table users (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(12) not null,
|
||||
username varchar(128) unique,
|
||||
primary_email varchar(128) unique,
|
||||
|
@ -18,5 +22,8 @@ create table users (
|
|||
primary key (id)
|
||||
);
|
||||
|
||||
create index users__created_at on users (created_at);
|
||||
create index users__name on users (name);
|
||||
create index users__id
|
||||
on users (tenant_id, id);
|
||||
|
||||
create index users__name
|
||||
on users (tenant_id, name);
|
||||
|
|
14
packages/schemas/tables/users_roles.sql
Normal file
14
packages/schemas/tables/users_roles.sql
Normal file
|
@ -0,0 +1,14 @@
|
|||
create table users_roles (
|
||||
id varchar(21) not null,
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
user_id varchar(21) not null
|
||||
references users (id) on update cascade on delete cascade,
|
||||
role_id varchar(21) not null
|
||||
references roles (id) on update cascade on delete cascade,
|
||||
primary key (id),
|
||||
constraint users_roles__user_id_role_id unique (tenant_id, user_id, role_id)
|
||||
);
|
||||
|
||||
create index users_roles__id
|
||||
on users_roles (tenant_id, id);
|
|
@ -1,5 +0,0 @@
|
|||
create table users_roles (
|
||||
user_id varchar(21) not null references users (id) on update cascade on delete cascade,
|
||||
role_id varchar(21) not null references roles (id) on update cascade on delete cascade,
|
||||
primary key (user_id, role_id)
|
||||
);
|
|
@ -590,7 +590,9 @@ importers:
|
|||
lint-staged: ^13.0.0
|
||||
pluralize: ^8.0.0
|
||||
prettier: ^2.8.1
|
||||
roarr: ^7.11.0
|
||||
slonik: ^30.0.0
|
||||
slonik-sql-tag-raw: ^1.1.4
|
||||
typescript: ^4.9.4
|
||||
zod: ^3.20.2
|
||||
dependencies:
|
||||
|
@ -614,7 +616,9 @@ importers:
|
|||
lint-staged: 13.0.0
|
||||
pluralize: 8.0.0
|
||||
prettier: 2.8.1
|
||||
roarr: 7.11.0
|
||||
slonik: 30.1.2
|
||||
slonik-sql-tag-raw: 1.1.4_roarr@7.11.0+slonik@30.1.2
|
||||
typescript: 4.9.4
|
||||
|
||||
packages/shared:
|
||||
|
@ -5893,19 +5897,12 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/define-properties/1.1.3:
|
||||
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
object-keys: 1.1.1
|
||||
|
||||
/define-properties/1.1.4:
|
||||
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
has-property-descriptors: 1.0.0
|
||||
object-keys: 1.1.1
|
||||
dev: true
|
||||
|
||||
/delay/4.4.1:
|
||||
resolution: {integrity: sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==}
|
||||
|
@ -7388,7 +7385,7 @@ packages:
|
|||
resolution: {integrity: sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
define-properties: 1.1.3
|
||||
define-properties: 1.1.4
|
||||
|
||||
/globalyzer/0.1.0:
|
||||
resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
|
||||
|
@ -7486,7 +7483,6 @@ packages:
|
|||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||
dependencies:
|
||||
get-intrinsic: 1.1.3
|
||||
dev: true
|
||||
|
||||
/has-symbols/1.0.3:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
|
@ -13177,7 +13173,6 @@ packages:
|
|||
roarr: 7.11.0
|
||||
serialize-error: 8.1.0
|
||||
slonik: 30.1.2
|
||||
dev: false
|
||||
|
||||
/slonik/22.7.1:
|
||||
resolution: {integrity: sha512-88GidNOWv4Bg0CqYLXajqcD0bbLip2soY6B4JzHP7EGDrWUb1WSlu7mIppTJVfcK99mx+jnX3xQq3FJ0DoOXag==}
|
||||
|
|
Loading…
Reference in a new issue