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,
|
managementResourceScope,
|
||||||
defaultRoleScopeRelation,
|
defaultRoleScopeRelation,
|
||||||
} from '@logto/schemas';
|
} from '@logto/schemas';
|
||||||
import { Hooks } from '@logto/schemas/models';
|
import { Hooks, Tenants } from '@logto/schemas/models';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import type { DatabasePool, DatabaseTransactionConnection } from 'slonik';
|
import type { DatabasePool, DatabaseTransactionConnection } from 'slonik';
|
||||||
import { sql } from 'slonik';
|
import { sql } from 'slonik';
|
||||||
|
@ -33,6 +33,31 @@ import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
||||||
import { getAlterationDirectory } from '../alteration/utils.js';
|
import { getAlterationDirectory } from '../alteration/utils.js';
|
||||||
import { oidcConfigReaders } from './oidc-config.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 createTables = async (connection: DatabaseTransactionConnection) => {
|
||||||
const tableDirectory = getPathInModule('@logto/schemas', 'tables');
|
const tableDirectory = getPathInModule('@logto/schemas', 'tables');
|
||||||
const directoryFiles = await readdir(tableDirectory);
|
const directoryFiles = await readdir(tableDirectory);
|
||||||
|
@ -44,16 +69,19 @@ const createTables = async (connection: DatabaseTransactionConnection) => {
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Await in loop is intended for better error handling
|
console.log(Tenants.raw, getExplicitOrder(Tenants.raw));
|
||||||
for (const [, query] of queries) {
|
|
||||||
|
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
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await connection.query(sql`${raw(query)}`);
|
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) => {
|
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), {
|
await oraPromise(createTables(connection), {
|
||||||
text: 'Create tables',
|
text: 'Create tables',
|
||||||
prefixText: chalk.blue('[info]'),
|
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",
|
"lint-staged": "^13.0.0",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"prettier": "^2.8.1",
|
"prettier": "^2.8.1",
|
||||||
|
"roarr": "^7.11.0",
|
||||||
"slonik": "^30.0.0",
|
"slonik": "^30.0.0",
|
||||||
|
"slonik-sql-tag-raw": "^1.1.4",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|
|
@ -44,8 +44,8 @@ const generate = async () => {
|
||||||
// Get statements
|
// Get statements
|
||||||
const statements = paragraph
|
const statements = paragraph
|
||||||
.split(';')
|
.split(';')
|
||||||
.map((value) => normalizeWhitespaces(value))
|
.map((value) => removeUnrecognizedComments(value))
|
||||||
.map((value) => removeUnrecognizedComments(value));
|
.map((value) => normalizeWhitespaces(value));
|
||||||
|
|
||||||
// Parse Table statements
|
// Parse Table statements
|
||||||
const tables = statements
|
const tables = statements
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createModel } from '@withtyped/server';
|
import { createModel } from '@withtyped/server';
|
||||||
|
|
||||||
export const Tenants = createModel(/* sql */ `
|
export const Tenants = createModel(/* sql */ `
|
||||||
|
/* init_order = 0 */
|
||||||
create table tenants (
|
create table tenants (
|
||||||
id varchar(32) not null,
|
id varchar(32) not null,
|
||||||
db_user_password varchar(128) not null,
|
db_user_password varchar(128) not null,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { CreateApplication } from '../db-entries/index.js';
|
import type { CreateApplication } from '../db-entries/index.js';
|
||||||
import { ApplicationType } from '../db-entries/index.js';
|
import { ApplicationType } from '../db-entries/index.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The fixed application ID for Admin Console.
|
* The fixed application ID for Admin Console.
|
||||||
|
@ -11,6 +12,7 @@ export const adminConsoleApplicationId = 'admin-console';
|
||||||
export const demoAppApplicationId = 'demo-app';
|
export const demoAppApplicationId = 'demo-app';
|
||||||
|
|
||||||
export const createDemoAppApplication = (secret: string): Readonly<CreateApplication> => ({
|
export const createDemoAppApplication = (secret: string): Readonly<CreateApplication> => ({
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: demoAppApplicationId,
|
id: demoAppApplicationId,
|
||||||
secret,
|
secret,
|
||||||
name: 'Demo App',
|
name: 'Demo App',
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import type { CreateResource } from '../db-entries/index.js';
|
import type { CreateResource } from '../db-entries/index.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
export const managementResourceId = 'management-api';
|
export const managementResourceId = 'management-api';
|
||||||
|
|
||||||
export const managementResource: Readonly<CreateResource> = Object.freeze({
|
export const managementResource: Readonly<CreateResource> = Object.freeze({
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: managementResourceId,
|
id: managementResourceId,
|
||||||
/**
|
/**
|
||||||
* The fixed resource indicator for Management APIs.
|
* The fixed resource indicator for Management APIs.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { CreateRole, CreateRolesScope } from '../db-entries/index.js';
|
import type { CreateRole, CreateRolesScope } from '../db-entries/index.js';
|
||||||
import { UserRole } from '../types/index.js';
|
import { UserRole } from '../types/index.js';
|
||||||
import { managementResourceScopeId } from './scope.js';
|
import { managementResourceScopeId } from './scope.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
export const adminConsoleAdminRoleId = 'ac-admin-id';
|
export const adminConsoleAdminRoleId = 'ac-admin-id';
|
||||||
|
|
||||||
|
@ -8,12 +9,14 @@ export const adminConsoleAdminRoleId = 'ac-admin-id';
|
||||||
* Default Admin Role for Admin Console.
|
* Default Admin Role for Admin Console.
|
||||||
*/
|
*/
|
||||||
export const defaultRole: Readonly<CreateRole> = {
|
export const defaultRole: Readonly<CreateRole> = {
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: adminConsoleAdminRoleId,
|
id: adminConsoleAdminRoleId,
|
||||||
name: UserRole.Admin,
|
name: UserRole.Admin,
|
||||||
description: 'Admin role for Logto.',
|
description: 'Admin role for Logto.',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultRoleScopeRelation: Readonly<CreateRolesScope> = {
|
export const defaultRoleScopeRelation: Readonly<CreateRolesScope> = {
|
||||||
|
tenantId: defaultTenantId,
|
||||||
roleId: adminConsoleAdminRoleId,
|
roleId: adminConsoleAdminRoleId,
|
||||||
scopeId: managementResourceScopeId,
|
scopeId: managementResourceScopeId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import type { CreateScope } from '../db-entries/index.js';
|
import type { CreateScope } from '../db-entries/index.js';
|
||||||
import { managementResourceId } from './resource.js';
|
import { managementResourceId } from './resource.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
export const managementResourceScopeId = 'management-api-scope';
|
export const managementResourceScopeId = 'management-api-scope';
|
||||||
|
|
||||||
export const managementResourceScope: Readonly<CreateScope> = Object.freeze({
|
export const managementResourceScope: Readonly<CreateScope> = Object.freeze({
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: managementResourceScopeId,
|
id: managementResourceScopeId,
|
||||||
name: 'management-api:default',
|
name: 'management-api:default',
|
||||||
description: 'Default scope for management API',
|
description: 'Default scope for management API',
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import type { CreateSetting } from '../db-entries/index.js';
|
import type { CreateSetting } from '../db-entries/index.js';
|
||||||
import { AppearanceMode } from '../foundations/index.js';
|
import { AppearanceMode } from '../foundations/index.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
export const defaultSettingId = 'default';
|
export const defaultSettingId = 'default';
|
||||||
|
|
||||||
export const createDefaultSetting = (): Readonly<CreateSetting> =>
|
export const createDefaultSetting = (): Readonly<CreateSetting> =>
|
||||||
Object.freeze({
|
Object.freeze({
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: defaultSettingId,
|
id: defaultSettingId,
|
||||||
adminConsole: {
|
adminConsole: {
|
||||||
language: 'en',
|
language: 'en',
|
||||||
|
|
|
@ -3,10 +3,12 @@ import { generateDarkColor } from '@logto/core-kit';
|
||||||
import type { CreateSignInExperience } from '../db-entries/index.js';
|
import type { CreateSignInExperience } from '../db-entries/index.js';
|
||||||
import { SignInMode } from '../db-entries/index.js';
|
import { SignInMode } from '../db-entries/index.js';
|
||||||
import { BrandingStyle, SignInIdentifier } from '../foundations/index.js';
|
import { BrandingStyle, SignInIdentifier } from '../foundations/index.js';
|
||||||
|
import { defaultTenantId } from './tenant.js';
|
||||||
|
|
||||||
const defaultPrimaryColor = '#6139F6';
|
const defaultPrimaryColor = '#6139F6';
|
||||||
|
|
||||||
export const defaultSignInExperience: Readonly<CreateSignInExperience> = {
|
export const defaultSignInExperience: Readonly<CreateSignInExperience> = {
|
||||||
|
tenantId: defaultTenantId,
|
||||||
id: 'default',
|
id: 'default',
|
||||||
color: {
|
color: {
|
||||||
primaryColor: defaultPrimaryColor,
|
primaryColor: defaultPrimaryColor,
|
||||||
|
@ -52,5 +54,6 @@ export const adminConsoleSignInExperience: CreateSignInExperience = {
|
||||||
style: BrandingStyle.Logo_Slogan,
|
style: BrandingStyle.Logo_Slogan,
|
||||||
logoUrl: 'https://logto.io/logo.svg',
|
logoUrl: 'https://logto.io/logo.svg',
|
||||||
darkLogoUrl: 'https://logto.io/logo-dark.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 type application_type as enum ('Native', 'SPA', 'Traditional', 'MachineToMachine');
|
||||||
|
|
||||||
create table applications (
|
create table applications (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(21) not null,
|
id varchar(21) not null,
|
||||||
name varchar(256) not null,
|
name varchar(256) not null,
|
||||||
secret varchar(64) not null,
|
secret varchar(64) not null,
|
||||||
|
@ -11,3 +15,6 @@ create table applications (
|
||||||
created_at timestamptz not null default(now()),
|
created_at timestamptz not null default(now()),
|
||||||
primary key (id)
|
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 (
|
create table connectors (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(128) not null,
|
id varchar(128) not null,
|
||||||
sync_profile boolean not null default FALSE,
|
sync_profile boolean not null default FALSE,
|
||||||
connector_id varchar(128) not null,
|
connector_id varchar(128) not null,
|
||||||
|
@ -7,3 +9,6 @@ create table connectors (
|
||||||
created_at timestamptz not null default(now()),
|
created_at timestamptz not null default(now()),
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create index connectors__id
|
||||||
|
on connectors (tenant_id, id);
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
create table custom_phrases (
|
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,
|
language_tag varchar(16) not null,
|
||||||
translation jsonb /* @use Translation */ 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,
|
id varchar(21) not null,
|
||||||
key varchar(128) not null,
|
key varchar(128) not null,
|
||||||
payload jsonb /* @use LogContextPayload */ not null default '{}'::jsonb,
|
payload jsonb /* @use LogContextPayload */ not null default '{}'::jsonb,
|
||||||
|
@ -7,7 +8,14 @@ create table logs
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create index logs__key on logs (key);
|
create index logs__id
|
||||||
create index logs__created_at on logs (created_at);
|
on logs (tenant_id, id);
|
||||||
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__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 (
|
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,
|
model_name varchar(64) not null,
|
||||||
id varchar(128) not null,
|
id varchar(128) not null,
|
||||||
payload jsonb /* @use OidcModelInstancePayload */ not null,
|
payload jsonb /* @use OidcModelInstancePayload */ not null,
|
||||||
expires_at timestamptz not null,
|
expires_at timestamptz not null,
|
||||||
consumed_at timestamptz,
|
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
|
create index oidc_model_instances__model_name_payload_user_code
|
||||||
on oidc_model_instances (
|
on oidc_model_instances (
|
||||||
model_name,
|
tenant_id,
|
||||||
(payload->>'userCode')
|
model_name,
|
||||||
);
|
(payload->>'userCode')
|
||||||
|
);
|
||||||
|
|
||||||
create index oidc_model_instances__model_name_payload_uid
|
create index oidc_model_instances__model_name_payload_uid
|
||||||
on oidc_model_instances (
|
on oidc_model_instances (
|
||||||
model_name,
|
tenant_id,
|
||||||
(payload->>'uid')
|
model_name,
|
||||||
);
|
(payload->>'uid')
|
||||||
|
);
|
||||||
|
|
||||||
create index oidc_model_instances__model_name_payload_grant_id
|
create index oidc_model_instances__model_name_payload_grant_id
|
||||||
on oidc_model_instances (
|
on oidc_model_instances (
|
||||||
model_name,
|
tenant_id,
|
||||||
(payload->>'grantId')
|
model_name,
|
||||||
);
|
(payload->>'grantId')
|
||||||
|
);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
create table passcodes (
|
create table passcodes (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(21) not null,
|
id varchar(21) not null,
|
||||||
interaction_jti varchar(128),
|
interaction_jti varchar(128),
|
||||||
phone varchar(32),
|
phone varchar(32),
|
||||||
|
@ -11,20 +13,14 @@ create table passcodes (
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create index passcodes__id
|
||||||
|
on passcodes (tenant_id, id);
|
||||||
|
|
||||||
create index passcodes__interaction_jti_type
|
create index passcodes__interaction_jti_type
|
||||||
on passcodes (
|
on passcodes (tenant_id, interaction_jti, type);
|
||||||
interaction_jti,
|
|
||||||
type
|
|
||||||
);
|
|
||||||
|
|
||||||
create index passcodes__email_type
|
create index passcodes__email_type
|
||||||
on passcodes (
|
on passcodes (tenant_id, email, type);
|
||||||
email,
|
|
||||||
type
|
|
||||||
);
|
|
||||||
|
|
||||||
create index passcodes__phone_type
|
create index passcodes__phone_type
|
||||||
on passcodes (
|
on passcodes (tenant_id, phone, type);
|
||||||
phone,
|
|
||||||
type
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
create table resources (
|
create table resources (
|
||||||
id varchar(21) not null,
|
tenant_id varchar(21) not null
|
||||||
name text not null,
|
references tenants (id) on update cascade on delete cascade,
|
||||||
indicator text not null unique, /* resource indicator also used as audience */
|
id varchar(21) not null,
|
||||||
access_token_ttl bigint not null default(3600), /* expiration value in seconds, default is 1h */
|
name text not null,
|
||||||
primary key (id)
|
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
|
create index resources__id
|
||||||
on resources (
|
on resources (tenant_id, indicator);
|
||||||
indicator
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
/* init_order = 1 */
|
||||||
|
|
||||||
create table roles (
|
create table roles (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(21) not null,
|
id varchar(21) not null,
|
||||||
name varchar(128) not null,
|
name varchar(128) not null,
|
||||||
description 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
|
create index roles__id
|
||||||
on roles (
|
on roles (tenant_id, id);
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
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 (
|
create table scopes (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(21) not null,
|
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,
|
name varchar(256) not null,
|
||||||
description text not null,
|
description text not null,
|
||||||
created_at timestamptz not null default(now()),
|
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
|
create index scopes__id
|
||||||
on scopes (
|
on scopes (tenant_id, id);
|
||||||
resource_id,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
|
|
|
@ -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 (
|
create table settings (
|
||||||
id varchar(21) not null,
|
tenant_id varchar(21) not null
|
||||||
admin_console jsonb /* @use AdminConsoleConfig */ not null,
|
references tenants (id) on update cascade on delete cascade,
|
||||||
primary key (id)
|
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 type sign_in_mode as enum ('SignIn', 'Register', 'SignInAndRegister');
|
||||||
|
|
||||||
create table sign_in_experiences (
|
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,
|
id varchar(21) not null,
|
||||||
color jsonb /* @use Color */ not null,
|
color jsonb /* @use Color */ not null,
|
||||||
branding jsonb /* @use Branding */ 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',
|
sign_in_mode sign_in_mode not null default 'SignInAndRegister',
|
||||||
primary key (id)
|
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 type users_password_encryption_method as enum ('Argon2i');
|
||||||
|
|
||||||
create table users (
|
create table users (
|
||||||
|
tenant_id varchar(21) not null
|
||||||
|
references tenants (id) on update cascade on delete cascade,
|
||||||
id varchar(12) not null,
|
id varchar(12) not null,
|
||||||
username varchar(128) unique,
|
username varchar(128) unique,
|
||||||
primary_email varchar(128) unique,
|
primary_email varchar(128) unique,
|
||||||
|
@ -18,5 +22,8 @@ create table users (
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create index users__created_at on users (created_at);
|
create index users__id
|
||||||
create index users__name on users (name);
|
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
|
lint-staged: ^13.0.0
|
||||||
pluralize: ^8.0.0
|
pluralize: ^8.0.0
|
||||||
prettier: ^2.8.1
|
prettier: ^2.8.1
|
||||||
|
roarr: ^7.11.0
|
||||||
slonik: ^30.0.0
|
slonik: ^30.0.0
|
||||||
|
slonik-sql-tag-raw: ^1.1.4
|
||||||
typescript: ^4.9.4
|
typescript: ^4.9.4
|
||||||
zod: ^3.20.2
|
zod: ^3.20.2
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -614,7 +616,9 @@ importers:
|
||||||
lint-staged: 13.0.0
|
lint-staged: 13.0.0
|
||||||
pluralize: 8.0.0
|
pluralize: 8.0.0
|
||||||
prettier: 2.8.1
|
prettier: 2.8.1
|
||||||
|
roarr: 7.11.0
|
||||||
slonik: 30.1.2
|
slonik: 30.1.2
|
||||||
|
slonik-sql-tag-raw: 1.1.4_roarr@7.11.0+slonik@30.1.2
|
||||||
typescript: 4.9.4
|
typescript: 4.9.4
|
||||||
|
|
||||||
packages/shared:
|
packages/shared:
|
||||||
|
@ -5893,19 +5897,12 @@ packages:
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
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:
|
/define-properties/1.1.4:
|
||||||
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-property-descriptors: 1.0.0
|
has-property-descriptors: 1.0.0
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/delay/4.4.1:
|
/delay/4.4.1:
|
||||||
resolution: {integrity: sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==}
|
resolution: {integrity: sha512-aL3AhqtfhOlT/3ai6sWXeqwnw63ATNpnUiN4HL7x9q+My5QtHlO3OIkasmug9LKzpheLdmUKGRKnYXYAS7FQkQ==}
|
||||||
|
@ -7388,7 +7385,7 @@ packages:
|
||||||
resolution: {integrity: sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==}
|
resolution: {integrity: sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
define-properties: 1.1.3
|
define-properties: 1.1.4
|
||||||
|
|
||||||
/globalyzer/0.1.0:
|
/globalyzer/0.1.0:
|
||||||
resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
|
resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
|
||||||
|
@ -7486,7 +7483,6 @@ packages:
|
||||||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.1.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has-symbols/1.0.3:
|
/has-symbols/1.0.3:
|
||||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||||
|
@ -13177,7 +13173,6 @@ packages:
|
||||||
roarr: 7.11.0
|
roarr: 7.11.0
|
||||||
serialize-error: 8.1.0
|
serialize-error: 8.1.0
|
||||||
slonik: 30.1.2
|
slonik: 30.1.2
|
||||||
dev: false
|
|
||||||
|
|
||||||
/slonik/22.7.1:
|
/slonik/22.7.1:
|
||||||
resolution: {integrity: sha512-88GidNOWv4Bg0CqYLXajqcD0bbLip2soY6B4JzHP7EGDrWUb1WSlu7mIppTJVfcK99mx+jnX3xQq3FJ0DoOXag==}
|
resolution: {integrity: sha512-88GidNOWv4Bg0CqYLXajqcD0bbLip2soY6B4JzHP7EGDrWUb1WSlu7mIppTJVfcK99mx+jnX3xQq3FJ0DoOXag==}
|
||||||
|
|
Loading…
Reference in a new issue