(core) move home server into core

Summary: This moves enough server material into core to run a home server.  The data engine is not yet incorporated (though in manual testing it works when ported).

Test Plan: existing tests pass

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2552
This commit is contained in:
Paul Fitzpatrick
2020-07-21 09:20:51 -04:00
parent c756f663ee
commit 5ef889addd
218 changed files with 33640 additions and 38 deletions

View File

@@ -0,0 +1,304 @@
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class Initial1536634251710 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
// TypeORM doesn't currently help with types of created tables:
// https://github.com/typeorm/typeorm/issues/305
// so we need to do a little smoothing over postgres and sqlite.
const sqlite = queryRunner.connection.driver.options.type === 'sqlite';
const datetime = sqlite ? "datetime" : "timestamp with time zone";
const now = "now()";
await queryRunner.createTable(new Table({
name: "users",
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: "api_key",
type: "varchar",
isNullable: true,
isUnique: true
}
]
}), false);
await queryRunner.createTable(new Table({
name: "orgs",
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: "domain",
type: "varchar",
isNullable: true,
},
{
name: "created_at",
type: datetime,
default: now
},
{
name: "updated_at",
type: datetime,
default: now
},
{
name: "owner_id",
type: "integer",
isNullable: true,
isUnique: true
}
],
foreignKeys: [
{
columnNames: ["owner_id"],
referencedColumnNames: ["id"],
referencedTableName: "users"
}
]
}), false);
await queryRunner.createTable(new Table({
name: "workspaces",
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: "created_at",
type: datetime,
default: now
},
{
name: "updated_at",
type: datetime,
default: now
},
{
name: "org_id",
type: "integer",
isNullable: true
}
],
foreignKeys: [
{
columnNames: ["org_id"],
referencedColumnNames: ["id"],
referencedTableName: "orgs"
}
]
}), false);
await queryRunner.createTable(new Table({
name: "docs",
columns: [
{
name: "id",
type: "varchar",
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: "created_at",
type: datetime,
default: now
},
{
name: "updated_at",
type: datetime,
default: now
},
{
name: "workspace_id",
type: "integer",
isNullable: true
}
],
foreignKeys: [
{
columnNames: ["workspace_id"],
referencedColumnNames: ["id"],
referencedTableName: "workspaces"
}
]
}), false);
await queryRunner.createTable(new Table({
name: "groups",
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: "name",
type: "varchar",
}
]
}), false);
await queryRunner.createTable(new Table({
name: "acl_rules",
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: "permissions",
type: "integer"
},
{
name: "type",
type: "varchar"
},
{
name: "workspace_id",
type: "integer",
isNullable: true
},
{
name: "org_id",
type: "integer",
isNullable: true
},
{
name: "doc_id",
type: "varchar",
isNullable: true
},
{
name: "group_id",
type: "integer",
isNullable: true
}
],
foreignKeys: [
{
columnNames: ["workspace_id"],
referencedColumnNames: ["id"],
referencedTableName: "workspaces"
},
{
columnNames: ["org_id"],
referencedColumnNames: ["id"],
referencedTableName: "orgs"
},
{
columnNames: ["doc_id"],
referencedColumnNames: ["id"],
referencedTableName: "docs"
},
{
columnNames: ["group_id"],
referencedColumnNames: ["id"],
referencedTableName: "groups"
}
]
}), false);
await queryRunner.createTable(new Table({
name: "group_users",
columns: [
{
name: "group_id",
type: "integer",
isPrimary: true
},
{
name: "user_id",
type: "integer",
isPrimary: true
},
],
foreignKeys: [
{
columnNames: ["group_id"],
referencedColumnNames: ["id"],
referencedTableName: "groups"
},
{
columnNames: ["user_id"],
referencedColumnNames: ["id"],
referencedTableName: "users"
}
]
}), false);
await queryRunner.createTable(new Table({
name: "group_groups",
columns: [
{
name: "group_id",
type: "integer",
isPrimary: true
},
{
name: "subgroup_id",
type: "integer",
isPrimary: true
},
],
foreignKeys: [
{
columnNames: ["group_id"],
referencedColumnNames: ["id"],
referencedTableName: "groups"
},
{
columnNames: ["subgroup_id"],
referencedColumnNames: ["id"],
referencedTableName: "groups"
}
]
}), false);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP TABLE "group_groups"`);
await queryRunner.query(`DROP TABLE "group_users"`);
await queryRunner.query(`DROP TABLE "acl_rules"`);
await queryRunner.query(`DROP TABLE "groups"`);
await queryRunner.query(`DROP TABLE "docs"`);
await queryRunner.query(`DROP TABLE "workspaces"`);
await queryRunner.query(`DROP TABLE "orgs"`);
await queryRunner.query(`DROP TABLE "users"`);
}
}

View File

@@ -0,0 +1,39 @@
import {MigrationInterface, QueryRunner, Table} from "typeorm";
export class Login1539031763952 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.createTable(new Table({
name: 'logins',
columns: [
{
name: "id",
type: "integer",
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: 'user_id',
type: 'integer'
},
{
name: 'email',
type: 'varchar',
isUnique: true
}
],
foreignKeys: [
{
columnNames: ["user_id"],
referencedColumnNames: ["id"],
referencedTableName: "users"
}
]
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query('DROP TABLE logins');
}
}

View File

@@ -0,0 +1,17 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
export class PinDocs1549313797109 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
const sqlite = queryRunner.connection.driver.options.type === 'sqlite';
await queryRunner.addColumn('docs', new TableColumn({
name: 'is_pinned',
type: 'boolean',
default: sqlite ? 0 : false
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn('docs', 'is_pinned');
}
}

View File

@@ -0,0 +1,16 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
export class UserPicture1549381727494 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.addColumn("users", new TableColumn({
name: "picture",
type: "varchar",
isNullable: true,
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn("users", "picture");
}
}

View File

@@ -0,0 +1,16 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
export class LoginDisplayEmail1551805156919 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.addColumn('logins', new TableColumn({
name: 'display_email',
type: 'varchar',
isNullable: true,
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn('logins', 'display_email');
}
}

View File

@@ -0,0 +1,32 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class LoginDisplayEmailNonNull1552416614755 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query('update logins set display_email = email where display_email is null');
// if our db will already heavily loaded, it might be better to add a check constraint
// rather than modifying the column properties. But for our case, this will be fast.
// To work correctly with RDS version of postgres, it is important to clone
// and change typeorm's settings for the column, rather than the settings specified
// in previous migrations. Otherwise typeorm will fall back on a brutal method of
// drop-and-recreate that doesn't work for non-null in any case.
//
// The pg command is very simple, just alter table logins alter column display_email set not null
// but sqlite migration is tedious since table needs to be rebuilt, so still just
// marginally worthwhile letting typeorm deal with it.
const logins = (await queryRunner.getTable('logins'))!;
const displayEmail = logins.findColumnByName('display_email')!;
const displayEmailNonNull = displayEmail.clone();
displayEmailNonNull.isNullable = false;
await queryRunner.changeColumn('logins', displayEmail, displayEmailNonNull);
}
public async down(queryRunner: QueryRunner): Promise<any> {
const logins = (await queryRunner.getTable('logins'))!;
const displayEmail = logins.findColumnByName('display_email')!;
const displayEmailNonNull = displayEmail.clone();
displayEmailNonNull.isNullable = true;
await queryRunner.changeColumn('logins', displayEmail, displayEmailNonNull);
}
}

View File

@@ -0,0 +1,66 @@
import {MigrationInterface, QueryRunner, TableIndex} from "typeorm";
export class Indexes1553016106336 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.createIndex("acl_rules", new TableIndex({
name: "acl_rules__org_id",
columnNames: ["org_id"]
}));
await queryRunner.createIndex("acl_rules", new TableIndex({
name: "acl_rules__workspace_id",
columnNames: ["workspace_id"]
}));
await queryRunner.createIndex("acl_rules", new TableIndex({
name: "acl_rules__doc_id",
columnNames: ["doc_id"]
}));
await queryRunner.createIndex("group_groups", new TableIndex({
name: "group_groups__group_id",
columnNames: ["group_id"]
}));
await queryRunner.createIndex("group_groups", new TableIndex({
name: "group_groups__subgroup_id",
columnNames: ["subgroup_id"]
}));
await queryRunner.createIndex("group_users", new TableIndex({
name: "group_users__group_id",
columnNames: ["group_id"]
}));
await queryRunner.createIndex("group_users", new TableIndex({
name: "group_users__user_id",
columnNames: ["user_id"]
}));
await queryRunner.createIndex("workspaces", new TableIndex({
name: "workspaces__org_id",
columnNames: ["org_id"]
}));
await queryRunner.createIndex("docs", new TableIndex({
name: "docs__workspace_id",
columnNames: ["workspace_id"]
}));
await queryRunner.createIndex("logins", new TableIndex({
name: "logins__user_id",
columnNames: ["user_id"]
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropIndex("acl_rules", "acl_rules__org_id");
await queryRunner.dropIndex("acl_rules", "acl_rules__workspace_id");
await queryRunner.dropIndex("acl_rules", "acl_rules__doc_id");
await queryRunner.dropIndex("group_groups", "group_groups__group_id");
await queryRunner.dropIndex("group_groups", "group_groups__subgroup_id");
await queryRunner.dropIndex("group_users", "group_users__group_id");
await queryRunner.dropIndex("group_users", "group_users__user_id");
await queryRunner.dropIndex("workspaces", "workspaces__org_id");
await queryRunner.dropIndex("docs", "docs__workspace_id");
await queryRunner.dropIndex("logins", "logins__user_id");
}
}

View File

@@ -0,0 +1,225 @@
import {MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey} from 'typeorm';
import {BillingAccount} from 'app/gen-server/entity/BillingAccount';
import {BillingAccountManager} from 'app/gen-server/entity/BillingAccountManager';
import {Organization} from 'app/gen-server/entity/Organization';
import {Product} from 'app/gen-server/entity/Product';
import {nativeValues} from 'app/gen-server/lib/values';
export class Billing1556726945436 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
// Create table for products.
await queryRunner.createTable(new Table({
name: 'products',
columns: [
{
name: 'id',
type: 'integer',
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: 'name',
type: 'varchar'
},
{
name: 'stripe_product_id',
type: 'varchar',
isUnique: true,
isNullable: true
},
{
name: 'features',
type: nativeValues.jsonType
}
]
}));
// Create a basic free product that existing orgs can use.
const product = new Product();
product.name = 'Free';
product.features = {};
await queryRunner.manager.save(product);
// Create billing accounts and billing account managers.
await queryRunner.createTable(new Table({
name: 'billing_accounts',
columns: [
{
name: 'id',
type: 'integer',
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: 'product_id',
type: 'integer'
},
{
name: 'individual',
type: nativeValues.booleanType
},
{
name: 'in_good_standing',
type: nativeValues.booleanType,
default: nativeValues.trueValue
},
{
name: 'status',
type: nativeValues.jsonType,
isNullable: true
},
{
name: 'stripe_customer_id',
type: 'varchar',
isUnique: true,
isNullable: true
},
{
name: 'stripe_subscription_id',
type: 'varchar',
isUnique: true,
isNullable: true
},
{
name: 'stripe_plan_id',
type: 'varchar',
isNullable: true
}
],
foreignKeys: [
{
columnNames: ['product_id'],
referencedColumnNames: ['id'],
referencedTableName: 'products'
}
]
}));
await queryRunner.createTable(new Table({
name: 'billing_account_managers',
columns: [
{
name: 'id',
type: 'integer',
isGenerated: true,
generationStrategy: 'increment',
isPrimary: true
},
{
name: 'billing_account_id',
type: 'integer'
},
{
name: 'user_id',
type: 'integer'
}
],
foreignKeys: [
{
columnNames: ['billing_account_id'],
referencedColumnNames: ['id'],
referencedTableName: 'billing_accounts',
onDelete: 'CASCADE' // delete manager if referenced billing_account goes away
},
{
columnNames: ['user_id'],
referencedColumnNames: ['id'],
referencedTableName: 'users',
onDelete: 'CASCADE' // delete manager if referenced user goes away
}
]
}));
// Add a reference to billing accounts from orgs.
await queryRunner.addColumn('orgs', new TableColumn({
name: 'billing_account_id',
type: 'integer',
isNullable: true
}));
await queryRunner.createForeignKey('orgs', new TableForeignKey({
columnNames: ['billing_account_id'],
referencedColumnNames: ['id'],
referencedTableName: 'billing_accounts'
}));
// Let's add billing accounts to all existing orgs.
// Personal orgs are put on an individual billing account.
// Other orgs are put on a team billing account, with the
// list of payment managers seeded by owners of that account.
const query =
queryRunner.manager.createQueryBuilder()
.select('orgs.id')
.from(Organization, 'orgs')
.leftJoin('orgs.owner', 'owners')
.addSelect('orgs.owner.id')
.leftJoinAndSelect('orgs.aclRules', 'acl_rules')
.leftJoinAndSelect('acl_rules.group', 'groups')
.leftJoin('groups.memberUsers', 'users')
.addSelect('users.id')
.where('permissions & 8 = 8'); // seed managers with owners+editors, omitting guests+viewers
// (permission 8 is "Remove")
const orgs = await query.getMany();
for (const org of orgs) {
const individual = Boolean(org.owner);
const billingAccountInsert = await queryRunner.manager.createQueryBuilder()
.insert()
.into(BillingAccount)
.values([{product, individual}])
.execute();
const billingAccountId = billingAccountInsert.identifiers[0].id;
if (individual) {
await queryRunner.manager.createQueryBuilder()
.insert()
.into(BillingAccountManager)
.values([{billingAccountId, userId: org.owner.id}])
.execute();
} else {
for (const rule of org.aclRules) {
for (const user of rule.group.memberUsers) {
await queryRunner.manager.createQueryBuilder()
.insert()
.into(BillingAccountManager)
.values([{billingAccountId, userId: user.id}])
.execute();
}
}
}
await queryRunner.manager.createQueryBuilder()
.update(Organization)
.set({billingAccountId})
.where('id = :id', {id: org.id})
.execute();
}
// TODO: in a future migration, orgs.billing_account_id could be constrained
// to be non-null. All code deployments linked to a database that will be
// migrated must have code that sets orgs.billing_account_id by that time,
// otherwise they would fail to create orgs (and remember creating a user
// involves creating an org).
/*
// Now that all orgs have a billing account (and this migration is running within
// a transaction), we can constrain orgs.billing_account_id to be non-null.
const orgTable = (await queryRunner.getTable('orgs'))!;
const billingAccountId = orgTable.findColumnByName('billing_account_id')!;
const billingAccountIdNonNull = billingAccountId.clone();
billingAccountIdNonNull.isNullable = false;
await queryRunner.changeColumn('orgs', billingAccountId, billingAccountIdNonNull);
*/
}
public async down(queryRunner: QueryRunner): Promise<any> {
// this is a bit ugly, but is the documented way to remove a foreign key
const table = await queryRunner.getTable('orgs');
const foreignKey = table!.foreignKeys.find(fk => fk.columnNames.indexOf('billing_account_id') !== -1);
await queryRunner.dropForeignKey('orgs', foreignKey!);
await queryRunner.dropColumn('orgs', 'billing_account_id');
await queryRunner.dropTable('billing_account_managers');
await queryRunner.dropTable('billing_accounts');
await queryRunner.dropTable('products');
}
}

View File

@@ -0,0 +1,27 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class OrgDomainUnique1557157922339 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
const logins = (await queryRunner.getTable('orgs'))!;
const domain = logins.findColumnByName('domain')!;
const domainUnique = domain.clone();
domainUnique.isUnique = true;
await queryRunner.changeColumn('orgs', domain, domainUnique);
// On postgres, all of the above amounts to:
// ALTER TABLE "orgs" ADD CONSTRAINT "..." UNIQUE ("domain")
// On sqlite, the table gets regenerated.
}
public async down(queryRunner: QueryRunner): Promise<any> {
const logins = (await queryRunner.getTable('orgs'))!;
const domain = logins.findColumnByName('domain')!;
const domainNonUnique = domain.clone();
domainNonUnique.isUnique = false;
await queryRunner.changeColumn('orgs', domain, domainNonUnique);
// On postgres, all of the above amount to:
// ALTER TABLE "orgs" DROP CONSTRAINT "..."
}
}

View File

@@ -0,0 +1,67 @@
import {MigrationInterface, QueryRunner, Table, TableColumn, TableIndex} from 'typeorm';
import {datetime, now} from 'app/gen-server/sqlUtils';
export class Aliases1561589211752 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
const dbType = queryRunner.connection.driver.options.type;
// Make a table for document aliases.
await queryRunner.createTable(new Table({
name: 'aliases',
columns: [
{
name: 'url_id',
type: 'varchar',
isPrimary: true
},
{
name: 'org_id',
type: 'integer',
isPrimary: true
},
{
name: 'doc_id',
type: 'varchar',
isNullable: true // nullable in case in future we make aliases for other resources
},
{
name: "created_at",
type: datetime(dbType),
default: now(dbType)
}
],
foreignKeys: [
{
columnNames: ['doc_id'],
referencedColumnNames: ['id'],
referencedTableName: 'docs',
onDelete: 'CASCADE' // delete alias if doc goes away
},
{
columnNames: ['org_id'],
referencedColumnNames: ['id'],
referencedTableName: 'orgs'
// no CASCADE set - let deletions be triggered via docs
}
]
}));
// Add preferred alias to docs. Not quite a foreign key (we'd need org as well)
await queryRunner.addColumn('docs', new TableColumn({
name: 'url_id',
type: 'varchar',
isNullable: true
}));
await queryRunner.createIndex("docs", new TableIndex({
name: "docs__url_id",
columnNames: ["url_id"]
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropIndex('docs', 'docs__url_id');
await queryRunner.dropColumn('docs', 'url_id');
await queryRunner.dropTable('aliases');
}
}

View File

@@ -0,0 +1,56 @@
import {MigrationInterface, QueryRunner} from "typeorm";
import * as roles from "app/common/roles";
import {AclRuleOrg} from "app/gen-server/entity/AclRule";
import {Group} from "app/gen-server/entity/Group";
import {Organization} from "app/gen-server/entity/Organization";
import {Permissions} from "app/gen-server/lib/Permissions";
export class TeamMembers1568238234987 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
// Get all orgs and add a team member ACL (with group) to each.
const orgs = await queryRunner.manager.createQueryBuilder()
.select("orgs.id")
.from(Organization, "orgs")
.getMany();
for (const org of orgs) {
const groupInsert = await queryRunner.manager.createQueryBuilder()
.insert()
.into(Group)
.values([{name: roles.MEMBER}])
.execute();
const groupId = groupInsert.identifiers[0].id;
await queryRunner.manager.createQueryBuilder()
.insert()
.into(AclRuleOrg)
.values([{
permissions: Permissions.VIEW,
organization: {id: org.id},
group: groupId
}])
.execute();
}
}
public async down(queryRunner: QueryRunner): Promise<any> {
// Remove all team member groups and corresponding ACLs.
const groups = await queryRunner.manager.createQueryBuilder()
.select("groups")
.from(Group, "groups")
.where('name = :name', {name: roles.MEMBER})
.getMany();
for (const group of groups) {
await queryRunner.manager.createQueryBuilder()
.delete()
.from(AclRuleOrg)
.where("group_id = :id", {id: group.id})
.execute();
}
await queryRunner.manager.createQueryBuilder()
.delete()
.from(Group)
.where("name = :name", {name: roles.MEMBER})
.execute();
}
}

View File

@@ -0,0 +1,18 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
export class FirstLogin1569593726320 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
const sqlite = queryRunner.connection.driver.options.type === 'sqlite';
const datetime = sqlite ? "datetime" : "timestamp with time zone";
await queryRunner.addColumn('users', new TableColumn({
name: 'first_login_at',
type: datetime,
isNullable: true
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn('users', 'first_login_at');
}
}

View File

@@ -0,0 +1,18 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
import {nativeValues} from 'app/gen-server/lib/values';
export class FirstTimeUser1569946508569 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.addColumn("users", new TableColumn({
name: "is_first_time_user",
type: nativeValues.booleanType,
default: nativeValues.falseValue,
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn("users", "is_first_time_user");
}
}

View File

@@ -0,0 +1,15 @@
import {MigrationInterface, QueryRunner, TableIndex} from "typeorm";
export class CustomerIndex1573569442552 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.createIndex("billing_accounts", new TableIndex({
name: "billing_accounts__stripe_customer_id",
columnNames: ["stripe_customer_id"]
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropIndex("billing_accounts", "billing_accounts__stripe_customer_id");
}
}

View File

@@ -0,0 +1,45 @@
import {MigrationInterface, QueryRunner, TableIndex} from "typeorm";
export class ExtraIndexes1579559983067 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.createIndex("acl_rules", new TableIndex({
name: "acl_rules__group_id",
columnNames: ["group_id"]
}));
await queryRunner.createIndex("orgs", new TableIndex({
name: "orgs__billing_account_id",
columnNames: ["billing_account_id"]
}));
await queryRunner.createIndex("billing_account_managers", new TableIndex({
name: "billing_account_managers__billing_account_id",
columnNames: ["billing_account_id"]
}));
await queryRunner.createIndex("billing_account_managers", new TableIndex({
name: "billing_account_managers__user_id",
columnNames: ["user_id"]
}));
await queryRunner.createIndex("billing_accounts", new TableIndex({
name: "billing_accounts__product_id",
columnNames: ["product_id"]
}));
await queryRunner.createIndex("aliases", new TableIndex({
name: "aliases__org_id",
columnNames: ["org_id"]
}));
await queryRunner.createIndex("aliases", new TableIndex({
name: "aliases__doc_id",
columnNames: ["doc_id"]
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropIndex("acl_rules", "acl_rules__group_id");
await queryRunner.dropIndex("orgs", "orgs__billing_account_id");
await queryRunner.dropIndex("billing_account_managers", "billing_account_managers__billing_account_id");
await queryRunner.dropIndex("billing_account_managers", "billing_account_managers__user_id");
await queryRunner.dropIndex("billing_accounts", "billing_accounts__product_id");
await queryRunner.dropIndex("aliases", "aliases__org_id");
await queryRunner.dropIndex("aliases", "aliases__doc_id");
}
}

View File

@@ -0,0 +1,17 @@
import {MigrationInterface, QueryRunner, TableColumn} from "typeorm";
export class OrgHost1591755411755 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.addColumn('orgs', new TableColumn({
name: 'host',
type: 'varchar',
isNullable: true,
isUnique: true
}));
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn('orgs', 'host');
}
}

View File

@@ -0,0 +1,26 @@
import {nativeValues} from "app/gen-server/lib/values";
import {MigrationInterface, QueryRunner, TableColumn, TableIndex} from "typeorm";
export class DocRemovedAt1592261300044 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
for (const table of ['docs', 'workspaces']) {
await queryRunner.addColumn(table, new TableColumn({
name: 'removed_at',
type: nativeValues.dateTimeType,
isNullable: true
}));
await queryRunner.createIndex(table, new TableIndex({
name: `${table}__removed_at`,
columnNames: ['removed_at']
}));
}
}
public async down(queryRunner: QueryRunner): Promise<any> {
for (const table of ['docs', 'workspaces']) {
await queryRunner.dropIndex(table, `${table}__removed_at`);
await queryRunner.dropColumn(table, 'removed_at');
}
}
}