mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(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:
304
app/gen-server/migration/1536634251710-Initial.ts
Normal file
304
app/gen-server/migration/1536634251710-Initial.ts
Normal 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"`);
|
||||
}
|
||||
}
|
||||
39
app/gen-server/migration/1539031763952-Login.ts
Normal file
39
app/gen-server/migration/1539031763952-Login.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
17
app/gen-server/migration/1549313797109-PinDocs.ts
Normal file
17
app/gen-server/migration/1549313797109-PinDocs.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
16
app/gen-server/migration/1549381727494-UserPicture.ts
Normal file
16
app/gen-server/migration/1549381727494-UserPicture.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
16
app/gen-server/migration/1551805156919-LoginDisplayEmail.ts
Normal file
16
app/gen-server/migration/1551805156919-LoginDisplayEmail.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
66
app/gen-server/migration/1553016106336-Indexes.ts
Normal file
66
app/gen-server/migration/1553016106336-Indexes.ts
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
225
app/gen-server/migration/1556726945436-Billing.ts
Normal file
225
app/gen-server/migration/1556726945436-Billing.ts
Normal 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');
|
||||
}
|
||||
|
||||
}
|
||||
27
app/gen-server/migration/1557157922339-OrgDomainUnique.ts
Normal file
27
app/gen-server/migration/1557157922339-OrgDomainUnique.ts
Normal 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 "..."
|
||||
}
|
||||
}
|
||||
67
app/gen-server/migration/1561589211752-Aliases.ts
Normal file
67
app/gen-server/migration/1561589211752-Aliases.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
56
app/gen-server/migration/1568238234987-TeamMembers.ts
Normal file
56
app/gen-server/migration/1568238234987-TeamMembers.ts
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
18
app/gen-server/migration/1569593726320-FirstLogin.ts
Normal file
18
app/gen-server/migration/1569593726320-FirstLogin.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
18
app/gen-server/migration/1569946508569-FirstTimeUser.ts
Normal file
18
app/gen-server/migration/1569946508569-FirstTimeUser.ts
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
15
app/gen-server/migration/1573569442552-CustomerIndex.ts
Normal file
15
app/gen-server/migration/1573569442552-CustomerIndex.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
45
app/gen-server/migration/1579559983067-ExtraIndexes.ts
Normal file
45
app/gen-server/migration/1579559983067-ExtraIndexes.ts
Normal 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");
|
||||
}
|
||||
}
|
||||
17
app/gen-server/migration/1591755411755-OrgHost.ts
Normal file
17
app/gen-server/migration/1591755411755-OrgHost.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
26
app/gen-server/migration/1592261300044-DocRemovedAt.ts
Normal file
26
app/gen-server/migration/1592261300044-DocRemovedAt.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user