Summary: Adding limits for AI calls and connecting those limits with a Stripe Account. - New table in homedb called `limits` - All calls to the AI are not routed through DocApi and measured. - All products now contain a special key `assistantLimit`, with a default value 0 - Limit is reset every time the subscription has changed its period - The billing page is updated with two new options that describe the AI plan - There is a new popup that allows the user to upgrade to a higher plan - Tiers are read directly from the Stripe product with a volume pricing model Test Plan: Updated and added Reviewers: georgegevoian, paulfitz Reviewed By: georgegevoian Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3907pull/563/head
parent
75d979abdb
commit
d13b9b9019
@ -0,0 +1,46 @@
|
|||||||
|
import {BaseEntity, Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn} from 'typeorm';
|
||||||
|
import {BillingAccount} from 'app/gen-server/entity/BillingAccount';
|
||||||
|
import {nativeValues} from 'app/gen-server/lib/values';
|
||||||
|
|
||||||
|
@Entity('limits')
|
||||||
|
export class Limit extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
public id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public limit: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public usage: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
public type: string;
|
||||||
|
|
||||||
|
@Column({name: 'billing_account_id'})
|
||||||
|
public billingAccountId: number;
|
||||||
|
|
||||||
|
@ManyToOne(type => BillingAccount)
|
||||||
|
@JoinColumn({name: 'billing_account_id'})
|
||||||
|
public billingAccount: BillingAccount;
|
||||||
|
|
||||||
|
@Column({name: 'created_at', default: () => "CURRENT_TIMESTAMP"})
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last time the Limit.limit value was changed, by an upgrade or downgrade. Null if it has never been changed.
|
||||||
|
*/
|
||||||
|
@Column({name: 'changed_at', type: nativeValues.dateTimeType, nullable: true})
|
||||||
|
public changedAt: Date|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last time the Limit.usage was used (by sending a request to the model). Null if it has never been used.
|
||||||
|
*/
|
||||||
|
@Column({name: 'used_at', type: nativeValues.dateTimeType, nullable: true})
|
||||||
|
public usedAt: Date|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last time the Limit.usage was reset, probably by billing cycle change. Null if it has never been reset.
|
||||||
|
*/
|
||||||
|
@Column({name: 'reset_at', type: nativeValues.dateTimeType, nullable: true})
|
||||||
|
public resetAt: Date|null;
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
import * as sqlUtils from "app/gen-server/sqlUtils";
|
||||||
|
import {MigrationInterface, QueryRunner, Table, TableIndex} from 'typeorm';
|
||||||
|
|
||||||
|
export class AssistantLimit1685343047786 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const dbType = queryRunner.connection.driver.options.type;
|
||||||
|
const datetime = sqlUtils.datetime(dbType);
|
||||||
|
const now = sqlUtils.now(dbType);
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: 'limits',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'integer',
|
||||||
|
isPrimary: true,
|
||||||
|
isGenerated: true,
|
||||||
|
generationStrategy: 'increment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'type',
|
||||||
|
type: 'varchar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'billing_account_id',
|
||||||
|
type: 'integer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'limit',
|
||||||
|
type: 'integer',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'usage',
|
||||||
|
type: 'integer',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "created_at",
|
||||||
|
type: datetime,
|
||||||
|
default: now
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "changed_at", // When the limit was last changed
|
||||||
|
type: datetime,
|
||||||
|
isNullable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "used_at", // When the usage was last increased
|
||||||
|
type: datetime,
|
||||||
|
isNullable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reset_at", // When the usage was last reset.
|
||||||
|
type: datetime,
|
||||||
|
isNullable: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
foreignKeys: [
|
||||||
|
{
|
||||||
|
columnNames: ['billing_account_id'],
|
||||||
|
referencedTableName: 'billing_accounts',
|
||||||
|
referencedColumnNames: ['id'],
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createIndex(
|
||||||
|
'limits',
|
||||||
|
new TableIndex({
|
||||||
|
name: 'limits_billing_account_id',
|
||||||
|
columnNames: ['billing_account_id'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.dropTable('limits');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue