From 8098a7ee5dd6480fd5edbd07bae019e9b3aa3e98 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 8 Mar 2023 20:22:06 +0000 Subject: [PATCH 1/3] feat: add checkIfLimitExceeded in UsageData model --- packages/backend/src/models/usage-data.ee.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/backend/src/models/usage-data.ee.ts b/packages/backend/src/models/usage-data.ee.ts index 558fe036..2e508f12 100644 --- a/packages/backend/src/models/usage-data.ee.ts +++ b/packages/backend/src/models/usage-data.ee.ts @@ -1,12 +1,14 @@ import { raw } from 'objection'; import Base from './base'; import User from './user'; +import PaymentPlan from './payment-plan.ee'; class UsageData extends Base { id!: string; userId!: string; consumedTaskCount!: number; nextResetAt!: string; + paymentPlan?: PaymentPlan; static tableName = 'usage_data'; @@ -31,8 +33,22 @@ class UsageData extends Base { to: 'users.id', }, }, + paymentPlan: { + relation: Base.BelongsToOneRelation, + modelClass: PaymentPlan, + join: { + from: 'usage_data.user_id', + to: 'payment_plans.user_id', + }, + }, }); + async checkIfLimitExceeded() { + const paymentPlan = await this.$relatedQuery('paymentPlan'); + + return this.consumedTaskCount >= paymentPlan.taskCount; + } + async increaseConsumedTaskCountByOne() { return await this.$query().patch({ consumedTaskCount: raw('consumed_task_count + 1') }); } From 92d1ed65ff5bfdb3974db45b6116d5d77a55cd71 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 8 Mar 2023 20:23:05 +0000 Subject: [PATCH 2/3] feat: expose errors from webhooks --- packages/backend/src/routes/webhooks.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/routes/webhooks.ts b/packages/backend/src/routes/webhooks.ts index cdf8f705..3bda1138 100644 --- a/packages/backend/src/routes/webhooks.ts +++ b/packages/backend/src/routes/webhooks.ts @@ -1,5 +1,6 @@ -import express, { Router } from 'express'; +import express, { Response, Router, NextFunction, RequestHandler } from 'express'; import multer from 'multer'; + import { IRequest } from '@automatisch/types'; import appConfig from '../config/app'; import webhookHandler from '../controllers/webhooks/handler'; @@ -16,9 +17,17 @@ router.use(express.text({ }, })); -router.get('/:flowId', webhookHandler); -router.put('/:flowId', webhookHandler); -router.patch('/:flowId', webhookHandler); -router.post('/:flowId', webhookHandler); +const exposeError = (handler: RequestHandler) => async (req: IRequest, res: Response, next: NextFunction) => { + try { + await handler(req, res, next); + } catch (err) { + res.send(err); + } +} + +router.get('/:flowId', exposeError(webhookHandler)); +router.put('/:flowId', exposeError(webhookHandler)); +router.patch('/:flowId', exposeError(webhookHandler)); +router.post('/:flowId', exposeError(webhookHandler)); export default router; From 54e68f62527e73bec3fad550944fb6cb91749e37 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 8 Mar 2023 20:24:59 +0000 Subject: [PATCH 3/3] feat: skip processing tasks over task quota --- .../backend/src/controllers/webhooks/handler.ts | 5 +++++ packages/backend/src/models/flow.ts | 16 ++++++++++++++++ packages/backend/src/routes/webhooks.ts | 2 +- packages/backend/src/services/action.ts | 13 ++++++++++--- packages/backend/src/services/flow.ts | 10 ++++++++-- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/controllers/webhooks/handler.ts b/packages/backend/src/controllers/webhooks/handler.ts index ed8684dd..5ac8a160 100644 --- a/packages/backend/src/controllers/webhooks/handler.ts +++ b/packages/backend/src/controllers/webhooks/handler.ts @@ -14,6 +14,11 @@ export default async (request: IRequest, response: Response) => { .throwIfNotFound(); const testRun = !flow.active; + + if (!testRun) { + await flow.throwIfQuotaExceeded(); + } + const triggerStep = await flow.getTriggerStep(); const triggerCommand = await triggerStep.getTriggerCommand(); const app = await triggerStep.getApp(); diff --git a/packages/backend/src/models/flow.ts b/packages/backend/src/models/flow.ts index e0868b97..7a96b6c1 100644 --- a/packages/backend/src/models/flow.ts +++ b/packages/backend/src/models/flow.ts @@ -1,5 +1,6 @@ import { ValidationError } from 'objection'; import type { ModelOptions, QueryContext } from 'objection'; +import appConfig from '../config/app'; import ExtendedQueryBuilder from './query-builder'; import Base from './base'; import Step from './step'; @@ -129,6 +130,21 @@ class Flow extends Base { type: 'trigger', }); } + + async throwIfQuotaExceeded() { + if (!appConfig.isCloud) return; + + const user = await this.$relatedQuery('user'); + const usageData = await user.$relatedQuery('usageData'); + + const hasExceeded = await usageData.checkIfLimitExceeded(); + + if (hasExceeded) { + throw new Error('The allowed task quota has been exhausted!'); + } + + return this; + } } export default Flow; diff --git a/packages/backend/src/routes/webhooks.ts b/packages/backend/src/routes/webhooks.ts index 3bda1138..98191a0a 100644 --- a/packages/backend/src/routes/webhooks.ts +++ b/packages/backend/src/routes/webhooks.ts @@ -21,7 +21,7 @@ const exposeError = (handler: RequestHandler) => async (req: IRequest, res: Resp try { await handler(req, res, next); } catch (err) { - res.send(err); + res.status(422).send(err); } } diff --git a/packages/backend/src/services/action.ts b/packages/backend/src/services/action.ts index f1d13373..79af8b9f 100644 --- a/packages/backend/src/services/action.ts +++ b/packages/backend/src/services/action.ts @@ -1,3 +1,4 @@ +import appConfig from '../config/app'; import Step from '../models/step'; import Flow from '../models/flow'; import Execution from '../models/execution'; @@ -17,17 +18,23 @@ type ProcessActionOptions = { export const processAction = async (options: ProcessActionOptions) => { const { flowId, stepId, executionId } = options; - const step = await Step.query().findById(stepId).throwIfNotFound(); + const flow = await Flow.query().findById(flowId).throwIfNotFound(); const execution = await Execution.query() .findById(executionId) .throwIfNotFound(); + if (!execution.testRun) { + await flow.throwIfQuotaExceeded(); + } + + const step = await Step.query().findById(stepId).throwIfNotFound(); + const $ = await globalVariable({ - flow: await Flow.query().findById(flowId).throwIfNotFound(), + flow, app: await step.getApp(), step: step, connection: await step.$relatedQuery('connection'), - execution: execution, + execution, }); const priorExecutionSteps = await ExecutionStep.query().where({ diff --git a/packages/backend/src/services/flow.ts b/packages/backend/src/services/flow.ts index 9ff6d6fc..4ace0599 100644 --- a/packages/backend/src/services/flow.ts +++ b/packages/backend/src/services/flow.ts @@ -1,3 +1,4 @@ +import appConfig from '../config/app'; import Flow from '../models/flow'; import globalVariable from '../helpers/global-variable'; import EarlyExitError from '../errors/early-exit'; @@ -10,7 +11,12 @@ type ProcessFlowOptions = { }; export const processFlow = async (options: ProcessFlowOptions) => { - const flow = await Flow.query().findById(options.flowId).throwIfNotFound(); + const { testRun, flowId } = options; + const flow = await Flow.query().findById(flowId).throwIfNotFound(); + + if (!testRun) { + await flow.throwIfQuotaExceeded(); + } const triggerStep = await flow.getTriggerStep(); const triggerCommand = await triggerStep.getTriggerCommand(); @@ -20,7 +26,7 @@ export const processFlow = async (options: ProcessFlowOptions) => { connection: await triggerStep.$relatedQuery('connection'), app: await triggerStep.getApp(), step: triggerStep, - testRun: options.testRun, + testRun, }); try {