From 44fc26b156b219a49cd95c4800a3ab2130e6a92e Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Thu, 2 Mar 2023 21:34:06 +0200 Subject: [PATCH 1/9] feat(server): deprecated the subscription module. --- .../src/api/controllers/Organization.ts | 6 +- .../api/controllers/Subscription/Licenses.ts | 250 ------------------ .../controllers/Subscription/PaymentMethod.ts | 31 --- .../Subscription/PaymentViaLicense.ts | 125 --------- .../src/api/controllers/Subscription/index.ts | 49 ---- packages/server/src/api/index.ts | 6 - .../api/middleware/SubscriptionMiddleware.ts | 41 --- ...091642_create_subscriptions_plans_table.js | 22 -- .../20200823234134_create_plans_table.js | 30 --- ...234434_create_subscription_plan_feature.js | 15 -- ...6_create_subscription_plan_subscription.js | 22 -- ...5339_create_subscription_licenses_table.js | 22 -- 12 files changed, 1 insertion(+), 618 deletions(-) delete mode 100644 packages/server/src/api/controllers/Subscription/Licenses.ts delete mode 100644 packages/server/src/api/controllers/Subscription/PaymentMethod.ts delete mode 100644 packages/server/src/api/controllers/Subscription/PaymentViaLicense.ts delete mode 100644 packages/server/src/api/controllers/Subscription/index.ts delete mode 100644 packages/server/src/api/middleware/SubscriptionMiddleware.ts delete mode 100644 packages/server/src/system/migrations/20200527091642_create_subscriptions_plans_table.js delete mode 100644 packages/server/src/system/migrations/20200823234134_create_plans_table.js delete mode 100644 packages/server/src/system/migrations/20200823234434_create_subscription_plan_feature.js delete mode 100644 packages/server/src/system/migrations/20200823234636_create_subscription_plan_subscription.js delete mode 100644 packages/server/src/system/migrations/20200823235339_create_subscription_licenses_table.js diff --git a/packages/server/src/api/controllers/Organization.ts b/packages/server/src/api/controllers/Organization.ts index f133ec922..76c8e72bb 100644 --- a/packages/server/src/api/controllers/Organization.ts +++ b/packages/server/src/api/controllers/Organization.ts @@ -6,7 +6,6 @@ import { check, ValidationChain } from 'express-validator'; import asyncMiddleware from '@/api/middleware/asyncMiddleware'; import JWTAuth from '@/api/middleware/jwtAuth'; import TenancyMiddleware from '@/api/middleware/TenancyMiddleware'; -import SubscriptionMiddleware from '@/api/middleware/SubscriptionMiddleware'; import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser'; import OrganizationService from '@/services/Organization/OrganizationService'; import { @@ -24,7 +23,7 @@ const ACCEPTED_LOCATIONS = ['libya']; @Service() export default class OrganizationController extends BaseController { @Inject() - organizationService: OrganizationService; + private organizationService: OrganizationService; /** * Router constructor. @@ -32,13 +31,10 @@ export default class OrganizationController extends BaseController { router() { const router = Router(); - // Should before build tenant database the user be authorized and - // most important than that, should be subscribed to any plan. router.use(JWTAuth); router.use(AttachCurrentTenantUser); router.use(TenancyMiddleware); - router.use('/build', SubscriptionMiddleware('main')); router.post( '/build', this.organizationValidationSchema, diff --git a/packages/server/src/api/controllers/Subscription/Licenses.ts b/packages/server/src/api/controllers/Subscription/Licenses.ts deleted file mode 100644 index cf483f1a1..000000000 --- a/packages/server/src/api/controllers/Subscription/Licenses.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Service, Inject } from 'typedi'; -import { Router, Request, Response, NextFunction } from 'express'; -import { check, oneOf, ValidationChain } from 'express-validator'; -import basicAuth from 'express-basic-auth'; -import config from '@/config'; -import { License } from '@/system/models'; -import { ServiceError } from '@/exceptions'; -import BaseController from '@/api/controllers/BaseController'; -import LicenseService from '@/services/Payment/License'; -import asyncMiddleware from '@/api/middleware/asyncMiddleware'; -import { ILicensesFilter, ISendLicenseDTO } from '@/interfaces'; - -@Service() -export default class LicensesController extends BaseController { - @Inject() - licenseService: LicenseService; - - /** - * Router constructor. - */ - router() { - const router = Router(); - - router.use( - basicAuth({ - users: { - [config.licensesAuth.user]: config.licensesAuth.password, - }, - challenge: true, - }) - ); - router.post( - '/generate', - this.generateLicenseSchema, - this.validationResult, - asyncMiddleware(this.generateLicense.bind(this)), - this.catchServiceErrors, - ); - router.post( - '/disable/:licenseId', - this.validationResult, - asyncMiddleware(this.disableLicense.bind(this)), - this.catchServiceErrors, - ); - router.post( - '/send', - this.sendLicenseSchemaValidation, - this.validationResult, - asyncMiddleware(this.sendLicense.bind(this)), - this.catchServiceErrors, - ); - router.delete( - '/:licenseId', - asyncMiddleware(this.deleteLicense.bind(this)), - this.catchServiceErrors, - ); - router.get('/', asyncMiddleware(this.listLicenses.bind(this))); - return router; - } - - /** - * Generate license validation schema. - */ - get generateLicenseSchema(): ValidationChain[] { - return [ - check('loop').exists().isNumeric().toInt(), - check('period').exists().isNumeric().toInt(), - check('period_interval') - .exists() - .isIn(['month', 'months', 'year', 'years', 'day', 'days']), - check('plan_slug').exists().trim().escape(), - ]; - } - - /** - * Specific license validation schema. - */ - get specificLicenseSchema(): ValidationChain[] { - return [ - oneOf( - [check('license_id').exists().isNumeric().toInt()], - [check('license_code').exists().isNumeric().toInt()] - ), - ]; - } - - /** - * Send license validation schema. - */ - get sendLicenseSchemaValidation(): ValidationChain[] { - return [ - check('period').exists().isNumeric(), - check('period_interval').exists().trim().escape(), - check('plan_slug').exists().trim().escape(), - oneOf([ - check('phone_number').exists().trim().escape(), - check('email').exists().trim().escape(), - ]), - ]; - } - - /** - * Generate licenses codes with given period in bulk. - * @param {Request} req - * @param {Response} res - * @return {Response} - */ - async generateLicense(req: Request, res: Response, next: Function) { - const { loop = 10, period, periodInterval, planSlug } = this.matchedBodyData( - req - ); - - try { - await this.licenseService.generateLicenses( - loop, - period, - periodInterval, - planSlug - ); - return res.status(200).send({ - code: 100, - type: 'LICENSEES.GENERATED.SUCCESSFULLY', - message: 'The licenses have been generated successfully.', - }); - } catch (error) { - next(error); - } - } - - /** - * Disable the given license on the storage. - * @param {Request} req - * @param {Response} res - * @return {Response} - */ - async disableLicense(req: Request, res: Response, next: Function) { - const { licenseId } = req.params; - - try { - await this.licenseService.disableLicense(licenseId); - - return res.status(200).send({ license_id: licenseId }); - } catch (error) { - next(error); - } - } - - /** - * Deletes the given license code on the storage. - * @param {Request} req - * @param {Response} res - * @return {Response} - */ - async deleteLicense(req: Request, res: Response, next: Function) { - const { licenseId } = req.params; - - try { - await this.licenseService.deleteLicense(licenseId); - - return res.status(200).send({ license_id: licenseId }); - } catch (error) { - next(error) - } - } - - /** - * Send license code in the given period to the customer via email or phone number - * @param {Request} req - * @param {Response} res - * @return {Response} - */ - async sendLicense(req: Request, res: Response, next: Function) { - const sendLicenseDTO: ISendLicenseDTO = this.matchedBodyData(req); - - try { - await this.licenseService.sendLicenseToCustomer(sendLicenseDTO); - - return res.status(200).send({ - status: 100, - code: 'LICENSE.CODE.SENT', - message: 'The license has been sent to the given customer.', - }); - } catch (error) { - next(error); - } - } - - /** - * Listing licenses. - * @param {Request} req - * @param {Response} res - */ - async listLicenses(req: Request, res: Response) { - const filter: ILicensesFilter = { - disabled: false, - used: false, - sent: false, - active: false, - ...req.query, - }; - const licenses = await License.query().onBuild((builder) => { - builder.modify('filter', filter); - builder.orderBy('createdAt', 'ASC'); - }); - return res.status(200).send({ licenses }); - } - - /** - * Catches all service errors. - */ - catchServiceErrors(error, req: Request, res: Response, next: NextFunction) { - if (error instanceof ServiceError) { - if (error.errorType === 'PLAN_NOT_FOUND') { - return res.status(400).send({ - errors: [{ - type: 'PLAN.NOT.FOUND', - code: 100, - message: 'The given plan not found.', - }], - }); - } - if (error.errorType === 'LICENSE_NOT_FOUND') { - return res.status(400).send({ - errors: [{ - type: 'LICENSE_NOT_FOUND', - code: 200, - message: 'The given license id not found.' - }], - }); - } - if (error.errorType === 'LICENSE_ALREADY_DISABLED') { - return res.status(400).send({ - errors: [{ - type: 'LICENSE.ALREADY.DISABLED', - code: 200, - message: 'License is already disabled.' - }], - }); - } - if (error.errorType === 'NO_AVALIABLE_LICENSE_CODE') { - return res.status(400).send({ - status: 110, - message: 'There is no licenses availiable right now with the given period and plan.', - code: 'NO.AVALIABLE.LICENSE.CODE', - }); - } - } - next(error); - } -} diff --git a/packages/server/src/api/controllers/Subscription/PaymentMethod.ts b/packages/server/src/api/controllers/Subscription/PaymentMethod.ts deleted file mode 100644 index 2c954c307..000000000 --- a/packages/server/src/api/controllers/Subscription/PaymentMethod.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Inject } from 'typedi'; -import { Request, Response } from 'express'; -import { Plan } from '@/system/models'; -import BaseController from '@/api/controllers/BaseController'; -import SubscriptionService from '@/services/Subscription/SubscriptionService'; - -export default class PaymentMethodController extends BaseController { - @Inject() - subscriptionService: SubscriptionService; - - /** - * Validate the given plan slug exists on the storage. - * - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - * - * @return {Response|void} - */ - async validatePlanSlugExistance(req: Request, res: Response, next: Function) { - const { planSlug } = this.matchedBodyData(req); - const foundPlan = await Plan.query().where('slug', planSlug).first(); - - if (!foundPlan) { - return res.status(400).send({ - errors: [{ type: 'PLAN.SLUG.NOT.EXISTS', code: 110 }], - }); - } - next(); - } -} \ No newline at end of file diff --git a/packages/server/src/api/controllers/Subscription/PaymentViaLicense.ts b/packages/server/src/api/controllers/Subscription/PaymentViaLicense.ts deleted file mode 100644 index 7cf07c656..000000000 --- a/packages/server/src/api/controllers/Subscription/PaymentViaLicense.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Inject, Service } from 'typedi'; -import { NextFunction, Router, Request, Response } from 'express'; -import { check } from 'express-validator'; -import asyncMiddleware from '@/api/middleware/asyncMiddleware'; -import PaymentMethodController from '@/api/controllers/Subscription/PaymentMethod'; -import { - NotAllowedChangeSubscriptionPlan, - NoPaymentModelWithPricedPlan, - PaymentAmountInvalidWithPlan, - PaymentInputInvalid, - VoucherCodeRequired, -} from '@/exceptions'; -import { ILicensePaymentModel } from '@/interfaces'; -import instance from 'tsyringe/dist/typings/dependency-container'; - -@Service() -export default class PaymentViaLicenseController extends PaymentMethodController { - @Inject('logger') - logger: any; - - /** - * Router constructor. - */ - router() { - const router = Router(); - - router.post( - '/payment', - this.paymentViaLicenseSchema, - this.validationResult, - asyncMiddleware(this.validatePlanSlugExistance.bind(this)), - asyncMiddleware(this.paymentViaLicense.bind(this)), - this.handleErrors, - ); - return router; - } - - /** - * Payment via license validation schema. - */ - get paymentViaLicenseSchema() { - return [ - check('plan_slug').exists().trim().escape(), - check('license_code').exists().trim().escape(), - ]; - } - - /** - * Handle the subscription payment via license code. - * @param {Request} req - * @param {Response} res - * @return {Response} - */ - async paymentViaLicense(req: Request, res: Response, next: Function) { - const { planSlug, licenseCode } = this.matchedBodyData(req); - const { tenant } = req; - - try { - const licenseModel: ILicensePaymentModel = { licenseCode }; - - await this.subscriptionService.subscriptionViaLicense( - tenant.id, - planSlug, - licenseModel - ); - - return res.status(200).send({ - type: 'success', - code: 'PAYMENT.SUCCESSFULLY.MADE', - message: 'Payment via license has been made successfully.', - }); - } catch (exception) { - next(exception); - } - } - - /** - * Handle service errors. - * @param {Error} error - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - */ - private handleErrors( - exception: Error, - req: Request, - res: Response, - next: NextFunction - ) { - const errorReasons = []; - - if (exception instanceof VoucherCodeRequired) { - errorReasons.push({ - type: 'VOUCHER_CODE_REQUIRED', - code: 100, - }); - } - if (exception instanceof NoPaymentModelWithPricedPlan) { - errorReasons.push({ - type: 'NO_PAYMENT_WITH_PRICED_PLAN', - code: 140, - }); - } - if (exception instanceof NotAllowedChangeSubscriptionPlan) { - errorReasons.push({ - type: 'NOT.ALLOWED.RENEW.SUBSCRIPTION.WHILE.ACTIVE', - code: 120, - }); - } - if (errorReasons.length > 0) { - return res.status(400).send({ errors: errorReasons }); - } - if (exception instanceof PaymentInputInvalid) { - return res.status(400).send({ - errors: [{ type: 'LICENSE.CODE.IS.INVALID', code: 120 }], - }); - } - if (exception instanceof PaymentAmountInvalidWithPlan) { - return res.status(400).send({ - errors: [{ type: 'LICENSE.NOT.FOR.GIVEN.PLAN' }], - }); - } - next(exception); - } -} diff --git a/packages/server/src/api/controllers/Subscription/index.ts b/packages/server/src/api/controllers/Subscription/index.ts deleted file mode 100644 index 6145e7551..000000000 --- a/packages/server/src/api/controllers/Subscription/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Router, Request, Response, NextFunction } from 'express'; -import { Container, Service, Inject } from 'typedi'; -import JWTAuth from '@/api/middleware/jwtAuth'; -import TenancyMiddleware from '@/api/middleware/TenancyMiddleware'; -import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser'; -import PaymentViaLicenseController from '@/api/controllers/Subscription/PaymentViaLicense'; -import SubscriptionService from '@/services/Subscription/SubscriptionService'; -import asyncMiddleware from '@/api/middleware/asyncMiddleware'; - -@Service() -export default class SubscriptionController { - @Inject() - subscriptionService: SubscriptionService; - - /** - * Router constructor. - */ - router() { - const router = Router(); - - router.use(JWTAuth); - router.use(AttachCurrentTenantUser); - router.use(TenancyMiddleware); - - router.use('/license', Container.get(PaymentViaLicenseController).router()); - router.get('/', asyncMiddleware(this.getSubscriptions.bind(this))); - - return router; - } - - /** - * Retrieve all subscriptions of the authenticated user's tenant. - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - */ - async getSubscriptions(req: Request, res: Response, next: NextFunction) { - const { tenantId } = req; - - try { - const subscriptions = await this.subscriptionService.getSubscriptions( - tenantId - ); - return res.status(200).send({ subscriptions }); - } catch (error) { - next(error); - } - } -} diff --git a/packages/server/src/api/index.ts b/packages/server/src/api/index.ts index 2eb04ec64..727a9e0ba 100644 --- a/packages/server/src/api/index.ts +++ b/packages/server/src/api/index.ts @@ -4,7 +4,6 @@ import { Container } from 'typedi'; // Middlewares import JWTAuth from '@/api/middleware/jwtAuth'; import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser'; -import SubscriptionMiddleware from '@/api/middleware/SubscriptionMiddleware'; import TenancyMiddleware from '@/api/middleware/TenancyMiddleware'; import EnsureTenantIsInitialized from '@/api/middleware/EnsureTenantIsInitialized'; import SettingsMiddleware from '@/api/middleware/SettingsMiddleware'; @@ -37,8 +36,6 @@ import Resources from './controllers/Resources'; import ExchangeRates from '@/api/controllers/ExchangeRates'; import Media from '@/api/controllers/Media'; import Ping from '@/api/controllers/Ping'; -import Subscription from '@/api/controllers/Subscription'; -import Licenses from '@/api/controllers/Subscription/Licenses'; import InventoryAdjustments from '@/api/controllers/Inventory/InventoryAdjustments'; import asyncRenderMiddleware from './middleware/AsyncRenderMiddleware'; import Jobs from './controllers/Jobs'; @@ -69,8 +66,6 @@ export default () => { app.use('/auth', Container.get(Authentication).router()); app.use('/invite', Container.get(InviteUsers).nonAuthRouter()); - app.use('/licenses', Container.get(Licenses).router()); - app.use('/subscription', Container.get(Subscription).router()); app.use('/organization', Container.get(Organization).router()); app.use('/ping', Container.get(Ping).router()); app.use('/jobs', Container.get(Jobs).router()); @@ -83,7 +78,6 @@ export default () => { dashboard.use(JWTAuth); dashboard.use(AttachCurrentTenantUser); dashboard.use(TenancyMiddleware); - dashboard.use(SubscriptionMiddleware('main')); dashboard.use(EnsureTenantIsInitialized); dashboard.use(SettingsMiddleware); dashboard.use(I18nAuthenticatedMiddlware); diff --git a/packages/server/src/api/middleware/SubscriptionMiddleware.ts b/packages/server/src/api/middleware/SubscriptionMiddleware.ts deleted file mode 100644 index ce7d45258..000000000 --- a/packages/server/src/api/middleware/SubscriptionMiddleware.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import { Container } from 'typedi'; - -export default (subscriptionSlug = 'main') => async ( - req: Request, - res: Response, - next: NextFunction -) => { - const { tenant, tenantId } = req; - const Logger = Container.get('logger'); - const { subscriptionRepository } = Container.get('repositories'); - - if (!tenant) { - throw new Error('Should load `TenancyMiddlware` before this middleware.'); - } - Logger.info('[subscription_middleware] trying get tenant main subscription.'); - const subscription = await subscriptionRepository.getBySlugInTenant( - subscriptionSlug, - tenantId - ); - // Validate in case there is no any already subscription. - if (!subscription) { - Logger.info('[subscription_middleware] tenant has no subscription.', { - tenantId, - }); - return res.boom.badRequest('Tenant has no subscription.', { - errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }], - }); - } - // Validate in case the subscription is inactive. - else if (subscription.inactive()) { - Logger.info( - '[subscription_middleware] tenant main subscription is expired.', - { tenantId } - ); - return res.boom.badRequest(null, { - errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }], - }); - } - next(); -}; diff --git a/packages/server/src/system/migrations/20200527091642_create_subscriptions_plans_table.js b/packages/server/src/system/migrations/20200527091642_create_subscriptions_plans_table.js deleted file mode 100644 index 09d890648..000000000 --- a/packages/server/src/system/migrations/20200527091642_create_subscriptions_plans_table.js +++ /dev/null @@ -1,22 +0,0 @@ - -exports.up = function(knex) { - return knex.schema.createTable('subscriptions_plans', table => { - table.increments(); - - table.string('name'); - table.string('description'); - table.decimal('price'); - table.string('currency', 3); - - table.integer('trial_period'); - table.string('trial_interval'); - - table.integer('invoice_period'); - table.string('invoice_interval'); - table.timestamps(); - }); -}; - -exports.down = function(knex) { - return knex.schema.dropTableIfExists('subscriptions_plans') -}; diff --git a/packages/server/src/system/migrations/20200823234134_create_plans_table.js b/packages/server/src/system/migrations/20200823234134_create_plans_table.js deleted file mode 100644 index 2fc61a43a..000000000 --- a/packages/server/src/system/migrations/20200823234134_create_plans_table.js +++ /dev/null @@ -1,30 +0,0 @@ - -exports.up = function(knex) { - return knex.schema.createTable('subscription_plans', table => { - table.increments(); - table.string('slug'); - table.string('name'); - table.string('desc'); - table.boolean('active'); - - table.decimal('price').unsigned(); - table.string('currency', 3); - - table.decimal('trial_period').nullable(); - table.string('trial_interval').nullable(); - - table.decimal('invoice_period').nullable(); - table.string('invoice_interval').nullable(); - - table.integer('index').unsigned(); - table.timestamps(); - }).then(() => { - return knex.seed.run({ - specific: 'seed_subscriptions_plans.js', - }); - }); -}; - -exports.down = function(knex) { - return knex.schema.dropTableIfExists('subscription_plans') -}; diff --git a/packages/server/src/system/migrations/20200823234434_create_subscription_plan_feature.js b/packages/server/src/system/migrations/20200823234434_create_subscription_plan_feature.js deleted file mode 100644 index 43fea2798..000000000 --- a/packages/server/src/system/migrations/20200823234434_create_subscription_plan_feature.js +++ /dev/null @@ -1,15 +0,0 @@ - -exports.up = function(knex) { - return knex.schema.createTable('subscription_plan_features', table => { - table.increments(); - table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans'); - table.string('slug'); - table.string('name'); - table.string('description'); - table.timestamps(); - }); -}; - -exports.down = function(knex) { - return knex.schema.dropTableIfExists('subscription_plan_features'); -}; diff --git a/packages/server/src/system/migrations/20200823234636_create_subscription_plan_subscription.js b/packages/server/src/system/migrations/20200823234636_create_subscription_plan_subscription.js deleted file mode 100644 index 267be4614..000000000 --- a/packages/server/src/system/migrations/20200823234636_create_subscription_plan_subscription.js +++ /dev/null @@ -1,22 +0,0 @@ - -exports.up = function(knex) { - return knex.schema.createTable('subscription_plan_subscriptions', table => { - table.increments('id'); - table.string('slug'); - - table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans'); - table.bigInteger('tenant_id').unsigned().index().references('id').inTable('tenants'); - - table.dateTime('starts_at').nullable(); - table.dateTime('ends_at').nullable(); - - table.dateTime('cancels_at').nullable(); - table.dateTime('canceled_at').nullable(); - - table.timestamps(); - }); -}; - -exports.down = function(knex) { - return knex.schema.dropTableIfExists('subscription_plan_subscriptions'); -}; diff --git a/packages/server/src/system/migrations/20200823235339_create_subscription_licenses_table.js b/packages/server/src/system/migrations/20200823235339_create_subscription_licenses_table.js deleted file mode 100644 index 6babd6f03..000000000 --- a/packages/server/src/system/migrations/20200823235339_create_subscription_licenses_table.js +++ /dev/null @@ -1,22 +0,0 @@ - -exports.up = function(knex) { - return knex.schema.createTable('subscription_licenses', (table) => { - table.increments(); - - table.string('license_code').unique().index(); - table.integer('plan_id').unsigned().index().references('id').inTable('subscription_plans'); - - table.integer('license_period').unsigned(); - table.string('period_interval'); - - table.dateTime('sent_at').index(); - table.dateTime('disabled_at').index(); - table.dateTime('used_at').index(); - - table.timestamps(); - }) -}; - -exports.down = function(knex) { - return knex.schema.dropTableIfExists('subscription_licenses'); -}; From 3b79ac66ae52cc596b7ee56dbfeea5cf010df1ad Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Thu, 2 Mar 2023 22:44:14 +0200 Subject: [PATCH 2/9] feat(server): deprecated the subscription module. --- .../NoPaymentModelWithPricedPlan.ts | 8 - .../NotAllowedChangeSubscriptionPlan.ts | 8 - .../PaymentAmountInvalidWithPlan.ts | 7 - .../src/exceptions/PaymentInputInvalid.ts | 3 - .../src/exceptions/VoucherCodeRequired.ts | 5 - packages/server/src/exceptions/index.ts | 12 +- .../src/jobs/MailNotificationSubscribeEnd.ts | 34 ---- .../src/jobs/MailNotificationTrialEnd.ts | 34 ---- .../src/jobs/SMSNotificationSubscribeEnd.ts | 28 --- .../src/jobs/SMSNotificationTrialEnd.ts | 28 --- packages/server/src/jobs/SendLicenseEmail.ts | 33 ---- packages/server/src/jobs/SendLicensePhone.ts | 33 ---- packages/server/src/loaders/jobs.ts | 28 --- .../server/src/loaders/systemRepositories.ts | 2 - .../Organization/OrganizationService.ts | 1 - .../server/src/services/Payment/License.ts | 185 ------------------ .../services/Payment/LicenseMailMessages.ts | 26 --- .../services/Payment/LicensePaymentMethod.ts | 67 ------- .../services/Payment/LicenseSMSMessages.ts | 17 -- .../src/services/Payment/PaymentMethod.ts | 6 - packages/server/src/services/Payment/index.ts | 22 --- .../src/services/Subscription/MailMessages.ts | 30 --- .../src/services/Subscription/SMSMessages.ts | 40 ---- .../src/services/Subscription/Subscription.ts | 80 -------- .../Subscription/SubscriptionPeriod.ts | 41 ---- .../Subscription/SubscriptionService.ts | 69 ------- .../Subscription/SubscriptionViaLicense.ts | 54 ----- .../system/models/Subscriptions/License.ts | 129 ------------ .../src/system/models/Subscriptions/Plan.ts | 82 -------- .../models/Subscriptions/PlanFeature.ts | 36 ---- .../models/Subscriptions/PlanSubscription.ts | 164 ---------------- packages/server/src/system/models/Tenant.ts | 81 +------- packages/server/src/system/models/index.ts | 17 +- .../repositories/SubscriptionRepository.ts | 26 --- .../server/src/system/repositories/index.ts | 7 +- packages/server/src/system/seeds/.gitkeep | 0 .../system/seeds/seed_subscriptions_plans.js | 66 ------- 37 files changed, 8 insertions(+), 1501 deletions(-) delete mode 100644 packages/server/src/exceptions/NoPaymentModelWithPricedPlan.ts delete mode 100644 packages/server/src/exceptions/NotAllowedChangeSubscriptionPlan.ts delete mode 100644 packages/server/src/exceptions/PaymentAmountInvalidWithPlan.ts delete mode 100644 packages/server/src/exceptions/PaymentInputInvalid.ts delete mode 100644 packages/server/src/exceptions/VoucherCodeRequired.ts delete mode 100644 packages/server/src/jobs/MailNotificationSubscribeEnd.ts delete mode 100644 packages/server/src/jobs/MailNotificationTrialEnd.ts delete mode 100644 packages/server/src/jobs/SMSNotificationSubscribeEnd.ts delete mode 100644 packages/server/src/jobs/SMSNotificationTrialEnd.ts delete mode 100644 packages/server/src/jobs/SendLicenseEmail.ts delete mode 100644 packages/server/src/jobs/SendLicensePhone.ts delete mode 100644 packages/server/src/services/Payment/License.ts delete mode 100644 packages/server/src/services/Payment/LicenseMailMessages.ts delete mode 100644 packages/server/src/services/Payment/LicensePaymentMethod.ts delete mode 100644 packages/server/src/services/Payment/LicenseSMSMessages.ts delete mode 100644 packages/server/src/services/Payment/PaymentMethod.ts delete mode 100644 packages/server/src/services/Payment/index.ts delete mode 100644 packages/server/src/services/Subscription/MailMessages.ts delete mode 100644 packages/server/src/services/Subscription/SMSMessages.ts delete mode 100644 packages/server/src/services/Subscription/Subscription.ts delete mode 100644 packages/server/src/services/Subscription/SubscriptionPeriod.ts delete mode 100644 packages/server/src/services/Subscription/SubscriptionService.ts delete mode 100644 packages/server/src/services/Subscription/SubscriptionViaLicense.ts delete mode 100644 packages/server/src/system/models/Subscriptions/License.ts delete mode 100644 packages/server/src/system/models/Subscriptions/Plan.ts delete mode 100644 packages/server/src/system/models/Subscriptions/PlanFeature.ts delete mode 100644 packages/server/src/system/models/Subscriptions/PlanSubscription.ts delete mode 100644 packages/server/src/system/repositories/SubscriptionRepository.ts create mode 100644 packages/server/src/system/seeds/.gitkeep delete mode 100644 packages/server/src/system/seeds/seed_subscriptions_plans.js diff --git a/packages/server/src/exceptions/NoPaymentModelWithPricedPlan.ts b/packages/server/src/exceptions/NoPaymentModelWithPricedPlan.ts deleted file mode 100644 index 938ec8b4a..000000000 --- a/packages/server/src/exceptions/NoPaymentModelWithPricedPlan.ts +++ /dev/null @@ -1,8 +0,0 @@ - - -export default class NoPaymentModelWithPricedPlan { - - constructor() { - - } -} \ No newline at end of file diff --git a/packages/server/src/exceptions/NotAllowedChangeSubscriptionPlan.ts b/packages/server/src/exceptions/NotAllowedChangeSubscriptionPlan.ts deleted file mode 100644 index 3c5380259..000000000 --- a/packages/server/src/exceptions/NotAllowedChangeSubscriptionPlan.ts +++ /dev/null @@ -1,8 +0,0 @@ - - -export default class NotAllowedChangeSubscriptionPlan { - - constructor() { - this.name = "NotAllowedChangeSubscriptionPlan"; - } -} \ No newline at end of file diff --git a/packages/server/src/exceptions/PaymentAmountInvalidWithPlan.ts b/packages/server/src/exceptions/PaymentAmountInvalidWithPlan.ts deleted file mode 100644 index 834e8cbe1..000000000 --- a/packages/server/src/exceptions/PaymentAmountInvalidWithPlan.ts +++ /dev/null @@ -1,7 +0,0 @@ - - -export default class PaymentAmountInvalidWithPlan{ - constructor() { - - } -} \ No newline at end of file diff --git a/packages/server/src/exceptions/PaymentInputInvalid.ts b/packages/server/src/exceptions/PaymentInputInvalid.ts deleted file mode 100644 index 2a024ff05..000000000 --- a/packages/server/src/exceptions/PaymentInputInvalid.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default class PaymentInputInvalid { - constructor() {} -} diff --git a/packages/server/src/exceptions/VoucherCodeRequired.ts b/packages/server/src/exceptions/VoucherCodeRequired.ts deleted file mode 100644 index a3bef6ff2..000000000 --- a/packages/server/src/exceptions/VoucherCodeRequired.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default class VoucherCodeRequired { - constructor() { - this.name = 'VoucherCodeRequired'; - } -} diff --git a/packages/server/src/exceptions/index.ts b/packages/server/src/exceptions/index.ts index a18746d02..53bb38897 100644 --- a/packages/server/src/exceptions/index.ts +++ b/packages/server/src/exceptions/index.ts @@ -1,25 +1,15 @@ -import NotAllowedChangeSubscriptionPlan from './NotAllowedChangeSubscriptionPlan'; import ServiceError from './ServiceError'; import ServiceErrors from './ServiceErrors'; -import NoPaymentModelWithPricedPlan from './NoPaymentModelWithPricedPlan'; -import PaymentInputInvalid from './PaymentInputInvalid'; -import PaymentAmountInvalidWithPlan from './PaymentAmountInvalidWithPlan'; import TenantAlreadyInitialized from './TenantAlreadyInitialized'; import TenantAlreadySeeded from './TenantAlreadySeeded'; import TenantDBAlreadyExists from './TenantDBAlreadyExists'; import TenantDatabaseNotBuilt from './TenantDatabaseNotBuilt'; -import VoucherCodeRequired from './VoucherCodeRequired'; export { - NotAllowedChangeSubscriptionPlan, - NoPaymentModelWithPricedPlan, - PaymentAmountInvalidWithPlan, ServiceError, ServiceErrors, - PaymentInputInvalid, TenantAlreadyInitialized, TenantAlreadySeeded, TenantDBAlreadyExists, TenantDatabaseNotBuilt, - VoucherCodeRequired, -}; \ No newline at end of file +}; diff --git a/packages/server/src/jobs/MailNotificationSubscribeEnd.ts b/packages/server/src/jobs/MailNotificationSubscribeEnd.ts deleted file mode 100644 index a2b54778b..000000000 --- a/packages/server/src/jobs/MailNotificationSubscribeEnd.ts +++ /dev/null @@ -1,34 +0,0 @@ -import Container from 'typedi'; -import SubscriptionService from '@/services/Subscription/Subscription'; - -export default class MailNotificationSubscribeEnd { - /** - * Job handler. - * @param {Job} job - - */ - handler(job) { - const { tenantId, phoneNumber, remainingDays } = job.attrs.data; - - const subscriptionService = Container.get(SubscriptionService); - const Logger = Container.get('logger'); - - Logger.info( - `Send mail notification subscription end soon - started: ${job.attrs.data}` - ); - - try { - subscriptionService.mailMessages.sendRemainingTrialPeriod( - phoneNumber, - remainingDays - ); - Logger.info( - `Send mail notification subscription end soon - finished: ${job.attrs.data}` - ); - } catch (error) { - Logger.info( - `Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}` - ); - done(e); - } - } -} diff --git a/packages/server/src/jobs/MailNotificationTrialEnd.ts b/packages/server/src/jobs/MailNotificationTrialEnd.ts deleted file mode 100644 index 82d8bd53c..000000000 --- a/packages/server/src/jobs/MailNotificationTrialEnd.ts +++ /dev/null @@ -1,34 +0,0 @@ -import Container from 'typedi'; -import SubscriptionService from '@/services/Subscription/Subscription'; - -export default class MailNotificationTrialEnd { - /** - * - * @param {Job} job - - */ - handler(job) { - const { tenantId, phoneNumber, remainingDays } = job.attrs.data; - - const subscriptionService = Container.get(SubscriptionService); - const Logger = Container.get('logger'); - - Logger.info( - `Send mail notification subscription end soon - started: ${job.attrs.data}` - ); - - try { - subscriptionService.mailMessages.sendRemainingTrialPeriod( - phoneNumber, - remainingDays - ); - Logger.info( - `Send mail notification subscription end soon - finished: ${job.attrs.data}` - ); - } catch (error) { - Logger.info( - `Send mail notification subscription end soon - failed: ${job.attrs.data}, error: ${e}` - ); - done(e); - } - } -} diff --git a/packages/server/src/jobs/SMSNotificationSubscribeEnd.ts b/packages/server/src/jobs/SMSNotificationSubscribeEnd.ts deleted file mode 100644 index d203c1d6b..000000000 --- a/packages/server/src/jobs/SMSNotificationSubscribeEnd.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Container from 'typedi'; -import SubscriptionService from '@/services/Subscription/Subscription'; - -export default class SMSNotificationSubscribeEnd { - - /** - * - * @param {Job}job - */ - handler(job) { - const { tenantId, phoneNumber, remainingDays } = job.attrs.data; - - const subscriptionService = Container.get(SubscriptionService); - const Logger = Container.get('logger'); - - Logger.info(`Send SMS notification subscription end soon - started: ${job.attrs.data}`); - - try { - subscriptionService.smsMessages.sendRemainingSubscriptionPeriod( - phoneNumber, remainingDays, - ); - Logger.info(`Send SMS notification subscription end soon - finished: ${job.attrs.data}`); - } catch(error) { - Logger.info(`Send SMS notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`); - done(e); - } - } -} \ No newline at end of file diff --git a/packages/server/src/jobs/SMSNotificationTrialEnd.ts b/packages/server/src/jobs/SMSNotificationTrialEnd.ts deleted file mode 100644 index a3e5c5420..000000000 --- a/packages/server/src/jobs/SMSNotificationTrialEnd.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Container from 'typedi'; -import SubscriptionService from '@/services/Subscription/Subscription'; - -export default class SMSNotificationTrialEnd { - - /** - * - * @param {Job}job - */ - handler(job) { - const { tenantId, phoneNumber, remainingDays } = job.attrs.data; - - const subscriptionService = Container.get(SubscriptionService); - const Logger = Container.get('logger'); - - Logger.info(`Send notification subscription end soon - started: ${job.attrs.data}`); - - try { - subscriptionService.smsMessages.sendRemainingTrialPeriod( - phoneNumber, remainingDays, - ); - Logger.info(`Send notification subscription end soon - finished: ${job.attrs.data}`); - } catch(error) { - Logger.info(`Send notification subscription end soon - failed: ${job.attrs.data}, error: ${e}`); - done(e); - } - } -} \ No newline at end of file diff --git a/packages/server/src/jobs/SendLicenseEmail.ts b/packages/server/src/jobs/SendLicenseEmail.ts deleted file mode 100644 index d28c07afe..000000000 --- a/packages/server/src/jobs/SendLicenseEmail.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Container } from 'typedi'; -import LicenseService from '@/services/Payment/License'; - -export default class SendLicenseViaEmailJob { - /** - * Constructor method. - * @param agenda - */ - constructor(agenda) { - agenda.define( - 'send-license-via-email', - { priority: 'high', concurrency: 1, }, - this.handler, - ); - } - - public async handler(job, done: Function): Promise { - const Logger = Container.get('logger'); - const licenseService = Container.get(LicenseService); - const { email, licenseCode } = job.attrs.data; - - Logger.info(`[send_license_via_mail] started: ${job.attrs.data}`); - - try { - await licenseService.mailMessages.sendMailLicense(licenseCode, email); - Logger.info(`[send_license_via_mail] completed: ${job.attrs.data}`); - done(); - } catch(e) { - Logger.error(`[send_license_via_mail] ${job.attrs.data}, error: ${e}`); - done(e); - } - } -} diff --git a/packages/server/src/jobs/SendLicensePhone.ts b/packages/server/src/jobs/SendLicensePhone.ts deleted file mode 100644 index adc5429fb..000000000 --- a/packages/server/src/jobs/SendLicensePhone.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Container } from 'typedi'; -import LicenseService from '@/services/Payment/License'; - -export default class SendLicenseViaPhoneJob { - /** - * Constructor method. - */ - constructor(agenda) { - agenda.define( - 'send-license-via-phone', - { priority: 'high', concurrency: 1, }, - this.handler, - ); - } - - public async handler(job, done: Function): Promise { - const { phoneNumber, licenseCode } = job.attrs.data; - - const Logger = Container.get('logger'); - const licenseService = Container.get(LicenseService); - - Logger.debug(`Send license via phone number - started: ${job.attrs.data}`); - - try { - await licenseService.smsMessages.sendLicenseSMSMessage(phoneNumber, licenseCode); - Logger.debug(`Send license via phone number - completed: ${job.attrs.data}`); - done(); - } catch(e) { - Logger.error(`Send license via phone number: ${job.attrs.data}, error: ${e}`); - done(e); - } - } -} diff --git a/packages/server/src/loaders/jobs.ts b/packages/server/src/loaders/jobs.ts index 448df7ec4..49acd77c2 100644 --- a/packages/server/src/loaders/jobs.ts +++ b/packages/server/src/loaders/jobs.ts @@ -4,12 +4,6 @@ import WelcomeSMSJob from 'jobs/WelcomeSMS'; import ResetPasswordMailJob from 'jobs/ResetPasswordMail'; import ComputeItemCost from 'jobs/ComputeItemCost'; import RewriteInvoicesJournalEntries from 'jobs/writeInvoicesJEntries'; -import SendLicenseViaPhoneJob from 'jobs/SendLicensePhone'; -import SendLicenseViaEmailJob from 'jobs/SendLicenseEmail'; -import SendSMSNotificationSubscribeEnd from 'jobs/SMSNotificationSubscribeEnd'; -import SendSMSNotificationTrialEnd from 'jobs/SMSNotificationTrialEnd'; -import SendMailNotificationSubscribeEnd from 'jobs/MailNotificationSubscribeEnd'; -import SendMailNotificationTrialEnd from 'jobs/MailNotificationTrialEnd'; import UserInviteMailJob from 'jobs/UserInviteMail'; import OrganizationSetupJob from 'jobs/OrganizationSetup'; import OrganizationUpgrade from 'jobs/OrganizationUpgrade'; @@ -20,33 +14,11 @@ export default ({ agenda }: { agenda: Agenda }) => { new ResetPasswordMailJob(agenda); new WelcomeSMSJob(agenda); new UserInviteMailJob(agenda); - new SendLicenseViaEmailJob(agenda); - new SendLicenseViaPhoneJob(agenda); new ComputeItemCost(agenda); new RewriteInvoicesJournalEntries(agenda); new OrganizationSetupJob(agenda); new OrganizationUpgrade(agenda); new SmsNotification(agenda); - agenda.define( - 'send-sms-notification-subscribe-end', - { priority: 'nromal', concurrency: 1, }, - new SendSMSNotificationSubscribeEnd().handler, - ); - agenda.define( - 'send-sms-notification-trial-end', - { priority: 'normal', concurrency: 1, }, - new SendSMSNotificationTrialEnd().handler, - ); - agenda.define( - 'send-mail-notification-subscribe-end', - { priority: 'high', concurrency: 1, }, - new SendMailNotificationSubscribeEnd().handler - ); - agenda.define( - 'send-mail-notification-trial-end', - { priority: 'high', concurrency: 1, }, - new SendMailNotificationTrialEnd().handler - ); agenda.start(); }; diff --git a/packages/server/src/loaders/systemRepositories.ts b/packages/server/src/loaders/systemRepositories.ts index 4d84583ca..f162df8f3 100644 --- a/packages/server/src/loaders/systemRepositories.ts +++ b/packages/server/src/loaders/systemRepositories.ts @@ -1,7 +1,6 @@ import Container from 'typedi'; import { SystemUserRepository, - SubscriptionRepository, TenantRepository, } from '@/system/repositories'; @@ -11,7 +10,6 @@ export default () => { return { systemUserRepository: new SystemUserRepository(knex, cache), - subscriptionRepository: new SubscriptionRepository(knex, cache), tenantRepository: new TenantRepository(knex, cache), }; } \ No newline at end of file diff --git a/packages/server/src/services/Organization/OrganizationService.ts b/packages/server/src/services/Organization/OrganizationService.ts index b4886b4af..32431a066 100644 --- a/packages/server/src/services/Organization/OrganizationService.ts +++ b/packages/server/src/services/Organization/OrganizationService.ts @@ -150,7 +150,6 @@ export default class OrganizationService { public async currentOrganization(tenantId: number): Promise { const tenant = await Tenant.query() .findById(tenantId) - .withGraphFetched('subscriptions') .withGraphFetched('metadata'); this.throwIfTenantNotExists(tenant); diff --git a/packages/server/src/services/Payment/License.ts b/packages/server/src/services/Payment/License.ts deleted file mode 100644 index 8e0fbc675..000000000 --- a/packages/server/src/services/Payment/License.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Service, Container, Inject } from 'typedi'; -import cryptoRandomString from 'crypto-random-string'; -import { times } from 'lodash'; -import { License, Plan } from '@/system/models'; -import { ILicense, ISendLicenseDTO } from '@/interfaces'; -import LicenseMailMessages from '@/services/Payment/LicenseMailMessages'; -import LicenseSMSMessages from '@/services/Payment/LicenseSMSMessages'; -import { ServiceError } from '@/exceptions'; - -const ERRORS = { - PLAN_NOT_FOUND: 'PLAN_NOT_FOUND', - LICENSE_NOT_FOUND: 'LICENSE_NOT_FOUND', - LICENSE_ALREADY_DISABLED: 'LICENSE_ALREADY_DISABLED', - NO_AVALIABLE_LICENSE_CODE: 'NO_AVALIABLE_LICENSE_CODE', -}; - -@Service() -export default class LicenseService { - @Inject() - smsMessages: LicenseSMSMessages; - - @Inject() - mailMessages: LicenseMailMessages; - - /** - * Validate the plan existance on the storage. - * @param {number} tenantId - - * @param {string} planSlug - Plan slug. - */ - private async getPlanOrThrowError(planSlug: string) { - const foundPlan = await Plan.query().where('slug', planSlug).first(); - - if (!foundPlan) { - throw new ServiceError(ERRORS.PLAN_NOT_FOUND); - } - return foundPlan; - } - - /** - * Valdiate the license existance on the storage. - * @param {number} licenseId - License id. - */ - private async getLicenseOrThrowError(licenseId: number) { - const foundLicense = await License.query().findById(licenseId); - - if (!foundLicense) { - throw new ServiceError(ERRORS.LICENSE_NOT_FOUND); - } - return foundLicense; - } - - /** - * Validates whether the license id is disabled. - * @param {ILicense} license - */ - private validateNotDisabledLicense(license: ILicense) { - if (license.disabledAt) { - throw new ServiceError(ERRORS.LICENSE_ALREADY_DISABLED); - } - } - - /** - * Generates the license code in the given period. - * @param {number} licensePeriod - * @return {Promise} - */ - public async generateLicense( - licensePeriod: number, - periodInterval: string = 'days', - planSlug: string - ): ILicense { - let licenseCode: string; - let repeat: boolean = true; - - // Retrieve plan or throw not found error. - const plan = await this.getPlanOrThrowError(planSlug); - - while (repeat) { - licenseCode = cryptoRandomString({ length: 10, type: 'numeric' }); - const foundLicenses = await License.query().where( - 'license_code', - licenseCode - ); - - if (foundLicenses.length === 0) { - repeat = false; - } - } - return License.query().insert({ - licenseCode, - licensePeriod, - periodInterval, - planId: plan.id, - }); - } - - /** - * Generates licenses. - * @param {number} loop - * @param {number} licensePeriod - * @param {string} periodInterval - * @param {number} planId - */ - public async generateLicenses( - loop = 1, - licensePeriod: number, - periodInterval: string = 'days', - planSlug: string - ) { - const asyncOpers: Promise[] = []; - - times(loop, () => { - const generateOper = this.generateLicense( - licensePeriod, - periodInterval, - planSlug - ); - asyncOpers.push(generateOper); - }); - return Promise.all(asyncOpers); - } - - /** - * Disables the given license id on the storage. - * @param {string} licenseSlug - License slug. - * @return {Promise} - */ - public async disableLicense(licenseId: number) { - const license = await this.getLicenseOrThrowError(licenseId); - - this.validateNotDisabledLicense(license); - - return License.markLicenseAsDisabled(license.id, 'id'); - } - - /** - * Deletes the given license id from the storage. - * @param licenseSlug {string} - License slug. - */ - public async deleteLicense(licenseSlug: string) { - const license = await this.getPlanOrThrowError(licenseSlug); - - return License.query().where('id', license.id).delete(); - } - - /** - * Sends license code to the given customer via SMS or mail message. - * @param {string} licenseCode - License code. - * @param {string} phoneNumber - Phone number. - * @param {string} email - Email address. - */ - public async sendLicenseToCustomer(sendLicense: ISendLicenseDTO) { - const agenda = Container.get('agenda'); - const { phoneNumber, email, period, periodInterval } = sendLicense; - - // Retreive plan details byt the given plan slug. - const plan = await this.getPlanOrThrowError(sendLicense.planSlug); - - const license = await License.query() - .modify('filterActiveLicense') - .where('license_period', period) - .where('period_interval', periodInterval) - .where('plan_id', plan.id) - .first(); - - if (!license) { - throw new ServiceError(ERRORS.NO_AVALIABLE_LICENSE_CODE) - } - // Mark the license as used. - await License.markLicenseAsSent(license.licenseCode); - - if (sendLicense.email) { - await agenda.schedule('1 second', 'send-license-via-email', { - licenseCode: license.licenseCode, - email, - }); - } - if (phoneNumber) { - await agenda.schedule('1 second', 'send-license-via-phone', { - licenseCode: license.licenseCode, - phoneNumber, - }); - } - } -} diff --git a/packages/server/src/services/Payment/LicenseMailMessages.ts b/packages/server/src/services/Payment/LicenseMailMessages.ts deleted file mode 100644 index a9b144629..000000000 --- a/packages/server/src/services/Payment/LicenseMailMessages.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Container } from 'typedi'; -import Mail from '@/lib/Mail'; -import config from '@/config'; -export default class SubscriptionMailMessages { - /** - * Send license code to the given mail address. - * @param {string} licenseCode - * @param {email} email - */ - public async sendMailLicense(licenseCode: string, email: string) { - const Logger = Container.get('logger'); - - const mail = new Mail() - .setView('mail/LicenseReceive.html') - .setSubject('Bigcapital - License code') - .setTo(email) - .setData({ - licenseCode, - successEmail: config.customerSuccess.email, - successPhoneNumber: config.customerSuccess.phoneNumber, - }); - - await mail.send(); - Logger.info('[license_mail] sent successfully.'); - } -} \ No newline at end of file diff --git a/packages/server/src/services/Payment/LicensePaymentMethod.ts b/packages/server/src/services/Payment/LicensePaymentMethod.ts deleted file mode 100644 index a8dbdf4ec..000000000 --- a/packages/server/src/services/Payment/LicensePaymentMethod.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { License } from '@/system/models'; -import PaymentMethod from '@/services/Payment/PaymentMethod'; -import { Plan } from '@/system/models'; -import { IPaymentMethod, ILicensePaymentModel } from '@/interfaces'; -import { - PaymentInputInvalid, - PaymentAmountInvalidWithPlan, - VoucherCodeRequired, -} from '@/exceptions'; - -export default class LicensePaymentMethod - extends PaymentMethod - implements IPaymentMethod -{ - /** - * Payment subscription of organization via license code. - * @param {ILicensePaymentModel} licensePaymentModel - - */ - public async payment(licensePaymentModel: ILicensePaymentModel, plan: Plan) { - this.validateLicensePaymentModel(licensePaymentModel); - - const license = await this.getLicenseOrThrowInvalid(licensePaymentModel); - this.validatePaymentAmountWithPlan(license, plan); - - // Mark the license code as used. - return License.markLicenseAsUsed(licensePaymentModel.licenseCode); - } - - /** - * Validates the license code activation on the storage. - * @param {ILicensePaymentModel} licensePaymentModel - - */ - private async getLicenseOrThrowInvalid( - licensePaymentModel: ILicensePaymentModel - ) { - const foundLicense = await License.query() - .modify('filterActiveLicense') - .where('license_code', licensePaymentModel.licenseCode) - .first(); - - if (!foundLicense) { - throw new PaymentInputInvalid(); - } - return foundLicense; - } - - /** - * Validates the payment amount with given plan price. - * @param {License} license - * @param {Plan} plan - */ - private validatePaymentAmountWithPlan(license: License, plan: Plan) { - if (license.planId !== plan.id) { - throw new PaymentAmountInvalidWithPlan(); - } - } - - /** - * Validate voucher payload. - * @param {ILicensePaymentModel} licenseModel - - */ - private validateLicensePaymentModel(licenseModel: ILicensePaymentModel) { - if (!licenseModel || !licenseModel.licenseCode) { - throw new VoucherCodeRequired(); - } - } -} diff --git a/packages/server/src/services/Payment/LicenseSMSMessages.ts b/packages/server/src/services/Payment/LicenseSMSMessages.ts deleted file mode 100644 index aa884ccdf..000000000 --- a/packages/server/src/services/Payment/LicenseSMSMessages.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Container, Inject } from 'typedi'; -import SMSClient from '@/services/SMSClient'; - -export default class SubscriptionSMSMessages { - @Inject('SMSClient') - smsClient: SMSClient; - - /** - * Sends license code to the given phone number via SMS message. - * @param {string} phoneNumber - * @param {string} licenseCode - */ - public async sendLicenseSMSMessage(phoneNumber: string, licenseCode: string) { - const message: string = `Your license card number: ${licenseCode}. If you need any help please contact us. Bigcapital.`; - return this.smsClient.sendMessage(phoneNumber, message); - } -} \ No newline at end of file diff --git a/packages/server/src/services/Payment/PaymentMethod.ts b/packages/server/src/services/Payment/PaymentMethod.ts deleted file mode 100644 index a0c1072f0..000000000 --- a/packages/server/src/services/Payment/PaymentMethod.ts +++ /dev/null @@ -1,6 +0,0 @@ -import moment from 'moment'; -import { IPaymentModel } from '@/interfaces'; - -export default class PaymentMethod implements IPaymentModel { - -} \ No newline at end of file diff --git a/packages/server/src/services/Payment/index.ts b/packages/server/src/services/Payment/index.ts deleted file mode 100644 index bec52feaf..000000000 --- a/packages/server/src/services/Payment/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { IPaymentMethod, IPaymentContext } from "interfaces"; -import { Plan } from '@/system/models'; - -export default class PaymentContext implements IPaymentContext{ - paymentMethod: IPaymentMethod; - - /** - * Constructor method. - * @param {IPaymentMethod} paymentMethod - */ - constructor(paymentMethod: IPaymentMethod) { - this.paymentMethod = paymentMethod; - } - - /** - * - * @param {} paymentModel - */ - makePayment(paymentModel: PaymentModel, plan: Plan) { - return this.paymentMethod.payment(paymentModel, plan); - } -} \ No newline at end of file diff --git a/packages/server/src/services/Subscription/MailMessages.ts b/packages/server/src/services/Subscription/MailMessages.ts deleted file mode 100644 index 4c50a5243..000000000 --- a/packages/server/src/services/Subscription/MailMessages.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Service } from "typedi"; - -@Service() -export default class SubscriptionMailMessages { - /** - * - * @param phoneNumber - * @param remainingDays - */ - public async sendRemainingSubscriptionPeriod(phoneNumber: string, remainingDays: number) { - const message: string = ` - Your remaining subscription is ${remainingDays} days, - please renew your subscription before expire. - `; - this.smsClient.sendMessage(phoneNumber, message); - } - - /** - * - * @param phoneNumber - * @param remainingDays - */ - public async sendRemainingTrialPeriod(phoneNumber: string, remainingDays: number) { - const message: string = ` - Your remaining free trial is ${remainingDays} days, - please subscription before ends, if you have any quation to contact us.`; - - this.smsClient.sendMessage(phoneNumber, message); - } -} \ No newline at end of file diff --git a/packages/server/src/services/Subscription/SMSMessages.ts b/packages/server/src/services/Subscription/SMSMessages.ts deleted file mode 100644 index 9cb7de273..000000000 --- a/packages/server/src/services/Subscription/SMSMessages.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Service, Inject } from 'typedi'; -import SMSClient from '@/services/SMSClient'; - -@Service() -export default class SubscriptionSMSMessages { - @Inject('SMSClient') - smsClient: SMSClient; - - /** - * Send remaining subscription period SMS message. - * @param {string} phoneNumber - - * @param {number} remainingDays - - */ - public async sendRemainingSubscriptionPeriod( - phoneNumber: string, - remainingDays: number - ): Promise { - const message: string = ` - Your remaining subscription is ${remainingDays} days, - please renew your subscription before expire. - `; - this.smsClient.sendMessage(phoneNumber, message); - } - - /** - * Send remaining trial period SMS message. - * @param {string} phoneNumber - - * @param {number} remainingDays - - */ - public async sendRemainingTrialPeriod( - phoneNumber: string, - remainingDays: number - ): Promise { - const message: string = ` - Your remaining free trial is ${remainingDays} days, - please subscription before ends, if you have any quation to contact us.`; - - this.smsClient.sendMessage(phoneNumber, message); - } -} diff --git a/packages/server/src/services/Subscription/Subscription.ts b/packages/server/src/services/Subscription/Subscription.ts deleted file mode 100644 index 4592a781f..000000000 --- a/packages/server/src/services/Subscription/Subscription.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Inject } from 'typedi'; -import { Tenant, Plan } from '@/system/models'; -import { IPaymentContext } from '@/interfaces'; -import { NotAllowedChangeSubscriptionPlan } from '@/exceptions'; - -export default class Subscription { - paymentContext: IPaymentContext | null; - - @Inject('logger') - logger: any; - - /** - * Constructor method. - * @param {IPaymentContext} - */ - constructor(payment?: IPaymentContext) { - this.paymentContext = payment; - } - - /** - * Give the tenant a new subscription. - * @param {Tenant} tenant - * @param {Plan} plan - * @param {string} invoiceInterval - * @param {number} invoicePeriod - * @param {string} subscriptionSlug - */ - protected async newSubscribtion( - tenant, - plan, - invoiceInterval: string, - invoicePeriod: number, - subscriptionSlug: string = 'main' - ) { - const subscription = await tenant - .$relatedQuery('subscriptions') - .modify('subscriptionBySlug', subscriptionSlug) - .first(); - - // No allowed to re-new the the subscription while the subscription is active. - if (subscription && subscription.active()) { - throw new NotAllowedChangeSubscriptionPlan(); - - // In case there is already subscription associated to the given tenant renew it. - } else if (subscription && subscription.inactive()) { - await subscription.renew(invoiceInterval, invoicePeriod); - - // No stored past tenant subscriptions create new one. - } else { - await tenant.newSubscription( - plan.id, - invoiceInterval, - invoicePeriod, - subscriptionSlug - ); - } - } - - /** - * Subscripe to the given plan. - * @param {Plan} plan - * @throws {NotAllowedChangeSubscriptionPlan} - */ - public async subscribe( - tenant: Tenant, - plan: Plan, - paymentModel?: PaymentModel, - subscriptionSlug: string = 'main' - ) { - await this.paymentContext.makePayment(paymentModel, plan); - - return this.newSubscribtion( - tenant, - plan, - plan.invoiceInterval, - plan.invoicePeriod, - subscriptionSlug - ); - } -} diff --git a/packages/server/src/services/Subscription/SubscriptionPeriod.ts b/packages/server/src/services/Subscription/SubscriptionPeriod.ts deleted file mode 100644 index c1d2e4a8b..000000000 --- a/packages/server/src/services/Subscription/SubscriptionPeriod.ts +++ /dev/null @@ -1,41 +0,0 @@ -import moment from 'moment'; - -export default class SubscriptionPeriod { - start: Date; - end: Date; - interval: string; - count: number; - - /** - * Constructor method. - * @param {string} interval - - * @param {number} count - - * @param {Date} start - - */ - constructor(interval: string = 'month', count: number, start?: Date) { - this.interval = interval; - this.count = count; - this.start = start; - - if (!start) { - this.start = moment().toDate(); - } - this.end = moment(start).add(count, interval).toDate(); - } - - getStartDate() { - return this.start; - } - - getEndDate() { - return this.end; - } - - getInterval() { - return this.interval; - } - - getIntervalCount() { - return this.interval; - } -} \ No newline at end of file diff --git a/packages/server/src/services/Subscription/SubscriptionService.ts b/packages/server/src/services/Subscription/SubscriptionService.ts deleted file mode 100644 index 0e254066b..000000000 --- a/packages/server/src/services/Subscription/SubscriptionService.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Service, Inject } from 'typedi'; -import { Plan, PlanSubscription, Tenant } from '@/system/models'; -import Subscription from '@/services/Subscription/Subscription'; -import LicensePaymentMethod from '@/services/Payment/LicensePaymentMethod'; -import PaymentContext from '@/services/Payment'; -import SubscriptionSMSMessages from '@/services/Subscription/SMSMessages'; -import SubscriptionMailMessages from '@/services/Subscription/MailMessages'; -import { ILicensePaymentModel } from '@/interfaces'; -import SubscriptionViaLicense from './SubscriptionViaLicense'; - -@Service() -export default class SubscriptionService { - @Inject() - smsMessages: SubscriptionSMSMessages; - - @Inject() - mailMessages: SubscriptionMailMessages; - - @Inject('logger') - logger: any; - - @Inject('repositories') - sysRepositories: any; - - /** - * Handles the payment process via license code and than subscribe to - * the given tenant. - * @param {number} tenantId - * @param {String} planSlug - * @param {string} licenseCode - * @return {Promise} - */ - public async subscriptionViaLicense( - tenantId: number, - planSlug: string, - paymentModel: ILicensePaymentModel, - subscriptionSlug: string = 'main' - ) { - // Retrieve plan details. - const plan = await Plan.query().findOne('slug', planSlug); - - // Retrieve tenant details. - const tenant = await Tenant.query().findById(tenantId); - - // License payment method. - const paymentViaLicense = new LicensePaymentMethod(); - - // Payment context. - const paymentContext = new PaymentContext(paymentViaLicense); - - // Subscription. - const subscription = new SubscriptionViaLicense(paymentContext); - - // Subscribe. - await subscription.subscribe(tenant, plan, paymentModel, subscriptionSlug); - } - - /** - * Retrieve all subscription of the given tenant. - * @param {number} tenantId - */ - public async getSubscriptions(tenantId: number) { - const subscriptions = await PlanSubscription.query().where( - 'tenant_id', - tenantId - ); - return subscriptions; - } -} diff --git a/packages/server/src/services/Subscription/SubscriptionViaLicense.ts b/packages/server/src/services/Subscription/SubscriptionViaLicense.ts deleted file mode 100644 index 629f30143..000000000 --- a/packages/server/src/services/Subscription/SubscriptionViaLicense.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { License, Tenant, Plan } from '@/system/models'; -import Subscription from './Subscription'; -import { PaymentModel } from '@/interfaces'; - -export default class SubscriptionViaLicense extends Subscription { - /** - * Subscripe to the given plan. - * @param {Plan} plan - * @throws {NotAllowedChangeSubscriptionPlan} - */ - public async subscribe( - tenant: Tenant, - plan: Plan, - paymentModel?: PaymentModel, - subscriptionSlug: string = 'main' - ): Promise { - await this.paymentContext.makePayment(paymentModel, plan); - - return this.newSubscriptionFromLicense( - tenant, - plan, - paymentModel.licenseCode, - subscriptionSlug - ); - } - - /** - * New subscription from the given license. - * @param {Tanant} tenant - * @param {Plab} plan - * @param {string} licenseCode - * @param {string} subscriptionSlug - * @returns {Promise} - */ - private async newSubscriptionFromLicense( - tenant, - plan, - licenseCode: string, - subscriptionSlug: string = 'main' - ): Promise { - // License information. - const licenseInfo = await License.query().findOne( - 'licenseCode', - licenseCode - ); - return this.newSubscribtion( - tenant, - plan, - licenseInfo.periodInterval, - licenseInfo.licensePeriod, - subscriptionSlug - ); - } -} diff --git a/packages/server/src/system/models/Subscriptions/License.ts b/packages/server/src/system/models/Subscriptions/License.ts deleted file mode 100644 index 97bbc87a7..000000000 --- a/packages/server/src/system/models/Subscriptions/License.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Model, mixin } from 'objection'; -import moment from 'moment'; -import SystemModel from '@/system/models/SystemModel'; - -export default class License extends SystemModel { - /** - * Table name. - */ - static get tableName() { - return 'subscription_licenses'; - } - - /** - * Timestamps columns. - */ - get timestamps() { - return ['createdAt', 'updatedAt']; - } - - /** - * Model modifiers. - */ - static get modifiers() { - return { - // Filters active licenses. - filterActiveLicense(query) { - query.where('disabled_at', null); - query.where('used_at', null); - }, - - // Find license by its code or id. - findByCodeOrId(query, id, code) { - if (id) { - query.where('id', id); - } - if (code) { - query.where('license_code', code); - } - }, - - // Filters licenses list. - filter(builder, licensesFilter) { - if (licensesFilter.active) { - builder.modify('filterActiveLicense'); - } - if (licensesFilter.disabled) { - builder.whereNot('disabled_at', null); - } - if (licensesFilter.used) { - builder.whereNot('used_at', null); - } - if (licensesFilter.sent) { - builder.whereNot('sent_at', null); - } - }, - }; - } - - /** - * Relationship mapping. - */ - static get relationMappings() { - const Plan = require('system/models/Subscriptions/Plan'); - - return { - plan: { - relation: Model.BelongsToOneRelation, - modelClass: Plan.default, - join: { - from: 'subscription_licenses.planId', - to: 'subscriptions_plans.id', - }, - }, - }; - } - - /** - * Deletes the given license code from the storage. - * @param {string} licenseCode - * @return {Promise} - */ - static deleteLicense(licenseCode, viaAttribute = 'license_code') { - return this.query().where(viaAttribute, licenseCode).delete(); - } - - /** - * Marks the given license code as disabled on the storage. - * @param {string} licenseCode - * @return {Promise} - */ - static markLicenseAsDisabled(licenseCode, viaAttribute = 'license_code') { - return this.query().where(viaAttribute, licenseCode).patch({ - disabled_at: moment().toMySqlDateTime(), - }); - } - - /** - * Marks the given license code as sent on the storage. - * @param {string} licenseCode - */ - static markLicenseAsSent(licenseCode, viaAttribute = 'license_code') { - return this.query().where(viaAttribute, licenseCode).patch({ - sent_at: moment().toMySqlDateTime(), - }); - } - - /** - * Marks the given license code as used on the storage. - * @param {string} licenseCode - * @return {Promise} - */ - static markLicenseAsUsed(licenseCode, viaAttribute = 'license_code') { - return this.query().where(viaAttribute, licenseCode).patch({ - used_at: moment().toMySqlDateTime(), - }); - } - - /** - * - * @param {IIPlan} plan - * @return {boolean} - */ - isEqualPlanPeriod(plan) { - return ( - this.invoicePeriod === plan.invoiceInterval && - license.licensePeriod === license.periodInterval - ); - } -} diff --git a/packages/server/src/system/models/Subscriptions/Plan.ts b/packages/server/src/system/models/Subscriptions/Plan.ts deleted file mode 100644 index 2b3f6b057..000000000 --- a/packages/server/src/system/models/Subscriptions/Plan.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Model, mixin } from 'objection'; -import SystemModel from '@/system/models/SystemModel'; -import { PlanSubscription } from '..'; - -export default class Plan extends mixin(SystemModel) { - /** - * Table name. - */ - static get tableName() { - return 'subscription_plans'; - } - - /** - * Timestamps columns. - */ - get timestamps() { - return ['createdAt', 'updatedAt']; - } - - /** - * Defined virtual attributes. - */ - static get virtualAttributes() { - return ['isFree', 'hasTrial']; - } - - /** - * Model modifiers. - */ - static get modifiers() { - return { - getFeatureBySlug(builder, featureSlug) { - builder.where('slug', featureSlug); - }, - }; - } - - /** - * Relationship mapping. - */ - static get relationMappings() { - const PlanSubscription = require('system/models/Subscriptions/PlanSubscription'); - - return { - /** - * The plan may have many subscriptions. - */ - subscriptions: { - relation: Model.HasManyRelation, - modelClass: PlanSubscription.default, - join: { - from: 'subscription_plans.id', - to: 'subscription_plan_subscriptions.planId', - }, - } - }; - } - - /** - * Check if plan is free. - * @return {boolean} - */ - isFree() { - return this.price <= 0; - } - - /** - * Check if plan is paid. - * @return {boolean} - */ - isPaid() { - return !this.isFree(); - } - - /** - * Check if plan has trial. - * @return {boolean} - */ - hasTrial() { - return this.trialPeriod && this.trialInterval; - } -} diff --git a/packages/server/src/system/models/Subscriptions/PlanFeature.ts b/packages/server/src/system/models/Subscriptions/PlanFeature.ts deleted file mode 100644 index 178fe818e..000000000 --- a/packages/server/src/system/models/Subscriptions/PlanFeature.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Model, mixin } from 'objection'; -import SystemModel from '@/system/models/SystemModel'; - -export default class PlanFeature extends mixin(SystemModel) { - /** - * Table name. - */ - static get tableName() { - return 'subscriptions.plan_features'; - } - - /** - * Timestamps columns. - */ - static get timestamps() { - return ['createdAt', 'updatedAt']; - } - - /** - * Relationship mapping. - */ - static get relationMappings() { - const Plan = require('system/models/Subscriptions/Plan'); - - return { - plan: { - relation: Model.BelongsToOneRelation, - modelClass: Plan.default, - join: { - from: 'subscriptions.plan_features.planId', - to: 'subscriptions.plans.id', - }, - }, - }; - } -} diff --git a/packages/server/src/system/models/Subscriptions/PlanSubscription.ts b/packages/server/src/system/models/Subscriptions/PlanSubscription.ts deleted file mode 100644 index d77ee6418..000000000 --- a/packages/server/src/system/models/Subscriptions/PlanSubscription.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Model, mixin } from 'objection'; -import SystemModel from '@/system/models/SystemModel'; -import moment from 'moment'; -import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod'; - -export default class PlanSubscription extends mixin(SystemModel) { - /** - * Table name. - */ - static get tableName() { - return 'subscription_plan_subscriptions'; - } - - /** - * Timestamps columns. - */ - get timestamps() { - return ['createdAt', 'updatedAt']; - } - - /** - * Defined virtual attributes. - */ - static get virtualAttributes() { - return ['active', 'inactive', 'ended', 'onTrial']; - } - - /** - * Modifiers queries. - */ - static get modifiers() { - return { - activeSubscriptions(builder) { - const dateFormat = 'YYYY-MM-DD HH:mm:ss'; - const now = moment().format(dateFormat); - - builder.where('ends_at', '>', now); - builder.where('trial_ends_at', '>', now); - }, - - inactiveSubscriptions() { - builder.modify('endedTrial'); - builder.modify('endedPeriod'); - }, - - subscriptionBySlug(builder, subscriptionSlug) { - builder.where('slug', subscriptionSlug); - }, - - endedTrial(builder) { - const dateFormat = 'YYYY-MM-DD HH:mm:ss'; - const endDate = moment().format(dateFormat); - - builder.where('ends_at', '<=', endDate); - }, - - endedPeriod(builder) { - const dateFormat = 'YYYY-MM-DD HH:mm:ss'; - const endDate = moment().format(dateFormat); - - builder.where('trial_ends_at', '<=', endDate); - }, - }; - } - - /** - * Relations mappings. - */ - static get relationMappings() { - const Tenant = require('system/models/Tenant'); - const Plan = require('system/models/Subscriptions/Plan'); - - return { - /** - * Plan subscription belongs to tenant. - */ - tenant: { - relation: Model.BelongsToOneRelation, - modelClass: Tenant.default, - join: { - from: 'subscription_plan_subscriptions.tenantId', - to: 'tenants.id', - }, - }, - - /** - * Plan description belongs to plan. - */ - plan: { - relation: Model.BelongsToOneRelation, - modelClass: Plan.default, - join: { - from: 'subscription_plan_subscriptions.planId', - to: 'subscription_plans.id', - }, - }, - }; - } - - /** - * Check if subscription is active. - * @return {Boolean} - */ - active() { - return !this.ended() || this.onTrial(); - } - - /** - * Check if subscription is inactive. - * @return {Boolean} - */ - inactive() { - return !this.active(); - } - - /** - * Check if subscription period has ended. - * @return {Boolean} - */ - ended() { - return this.endsAt ? moment().isAfter(this.endsAt) : false; - } - - /** - * Check if subscription is currently on trial. - * @return {Boolean} - */ - onTrial() { - return this.trailEndsAt ? moment().isAfter(this.trailEndsAt) : false; - } - - /** - * Set new period from the given details. - * @param {string} invoiceInterval - * @param {number} invoicePeriod - * @param {string} start - * - * @return {Object} - */ - static setNewPeriod(invoiceInterval, invoicePeriod, start) { - const period = new SubscriptionPeriod( - invoiceInterval, - invoicePeriod, - start, - ); - - const startsAt = period.getStartDate(); - const endsAt = period.getEndDate(); - - return { startsAt, endsAt }; - } - - /** - * Renews subscription period. - * @Promise - */ - renew(invoiceInterval, invoicePeriod) { - const { startsAt, endsAt } = PlanSubscription.setNewPeriod( - invoiceInterval, - invoicePeriod, - ); - return this.$query().update({ startsAt, endsAt }); - } -} diff --git a/packages/server/src/system/models/Tenant.ts b/packages/server/src/system/models/Tenant.ts index 82cb0d112..53436cd17 100644 --- a/packages/server/src/system/models/Tenant.ts +++ b/packages/server/src/system/models/Tenant.ts @@ -1,10 +1,8 @@ import moment from 'moment'; import { Model } from 'objection'; import uniqid from 'uniqid'; -import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod'; import BaseModel from 'models/Model'; import TenantMetadata from './TenantMetadata'; -import PlanSubscription from './Subscriptions/PlanSubscription'; export default class Tenant extends BaseModel { /** @@ -49,33 +47,13 @@ export default class Tenant extends BaseModel { return !!this.upgradeJobId; } - /** - * Query modifiers. - */ - static modifiers() { - return { - subscriptions(builder) { - builder.withGraphFetched('subscriptions'); - }, - }; - } - /** * Relations mappings. */ static get relationMappings() { - const PlanSubscription = require('./Subscriptions/PlanSubscription'); const TenantMetadata = require('./TenantMetadata'); return { - subscriptions: { - relation: Model.HasManyRelation, - modelClass: PlanSubscription.default, - join: { - from: 'tenants.id', - to: 'subscription_plan_subscriptions.tenantId', - }, - }, metadata: { relation: Model.HasOneRelation, modelClass: TenantMetadata.default, @@ -86,55 +64,6 @@ export default class Tenant extends BaseModel { }, }; } - - /** - * Retrieve the subscribed plans ids. - * @return {number[]} - */ - async subscribedPlansIds() { - const { subscriptions } = this; - return chain(subscriptions).map('planId').unq(); - } - - /** - * - * @param {*} planId - * @param {*} invoiceInterval - * @param {*} invoicePeriod - * @param {*} subscriptionSlug - * @returns - */ - newSubscription(planId, invoiceInterval, invoicePeriod, subscriptionSlug) { - return Tenant.newSubscription( - this.id, - planId, - invoiceInterval, - invoicePeriod, - subscriptionSlug, - ); - } - - /** - * Records a new subscription for the associated tenant. - */ - static newSubscription( - tenantId, - planId, - invoiceInterval, - invoicePeriod, - subscriptionSlug - ) { - const period = new SubscriptionPeriod(invoiceInterval, invoicePeriod); - - return PlanSubscription.query().insert({ - tenantId, - slug: subscriptionSlug, - planId, - startsAt: period.getStartDate(), - endsAt: period.getEndDate(), - }); - } - /** * Creates a new tenant with random organization id. */ @@ -185,9 +114,9 @@ export default class Tenant extends BaseModel { /** * Marks the given tenant as upgrading. - * @param {number} tenantId - * @param {string} upgradeJobId - * @returns + * @param {number} tenantId + * @param {string} upgradeJobId + * @returns */ static markAsUpgrading(tenantId, upgradeJobId) { return this.query().update({ upgradeJobId }).where({ id: tenantId }); @@ -195,8 +124,8 @@ export default class Tenant extends BaseModel { /** * Markes the given tenant as upgraded. - * @param {number} tenantId - * @returns + * @param {number} tenantId + * @returns */ static markAsUpgraded(tenantId) { return this.query().update({ upgradeJobId: null }).where({ id: tenantId }); diff --git a/packages/server/src/system/models/index.ts b/packages/server/src/system/models/index.ts index 6e5ee2d80..44a92ea27 100644 --- a/packages/server/src/system/models/index.ts +++ b/packages/server/src/system/models/index.ts @@ -1,22 +1,7 @@ - -import Plan from './Subscriptions/Plan'; -import PlanFeature from './Subscriptions/PlanFeature'; -import PlanSubscription from './Subscriptions/PlanSubscription'; -import License from './Subscriptions/License'; import Tenant from './Tenant'; import TenantMetadata from './TenantMetadata'; import SystemUser from './SystemUser'; import PasswordReset from './PasswordReset'; import Invite from './Invite'; -export { - Plan, - PlanFeature, - PlanSubscription, - License, - Tenant, - TenantMetadata, - SystemUser, - PasswordReset, - Invite, -} \ No newline at end of file +export { Tenant, TenantMetadata, SystemUser, PasswordReset, Invite }; diff --git a/packages/server/src/system/repositories/SubscriptionRepository.ts b/packages/server/src/system/repositories/SubscriptionRepository.ts deleted file mode 100644 index 44962b0b8..000000000 --- a/packages/server/src/system/repositories/SubscriptionRepository.ts +++ /dev/null @@ -1,26 +0,0 @@ -import SystemRepository from '@/system/repositories/SystemRepository'; -import { PlanSubscription } from '@/system/models'; - -export default class SubscriptionRepository extends SystemRepository { - /** - * Gets the repository's model. - */ - get model() { - return PlanSubscription.bindKnex(this.knex); - } - - /** - * Retrieve subscription from a given slug in specific tenant. - * @param {string} slug - * @param {number} tenantId - */ - getBySlugInTenant(slug: string, tenantId: number) { - const cacheKey = this.getCacheKey('getBySlugInTenant', slug, tenantId); - - return this.cache.get(cacheKey, () => { - return PlanSubscription.query() - .findOne('slug', slug) - .where('tenant_id', tenantId); - }); - } -} diff --git a/packages/server/src/system/repositories/index.ts b/packages/server/src/system/repositories/index.ts index 9fb001718..472f9867d 100644 --- a/packages/server/src/system/repositories/index.ts +++ b/packages/server/src/system/repositories/index.ts @@ -1,9 +1,4 @@ import SystemUserRepository from '@/system/repositories/SystemUserRepository'; -import SubscriptionRepository from '@/system/repositories/SubscriptionRepository'; import TenantRepository from '@/system/repositories/TenantRepository'; -export { - SystemUserRepository, - SubscriptionRepository, - TenantRepository, -}; \ No newline at end of file +export { SystemUserRepository, TenantRepository }; diff --git a/packages/server/src/system/seeds/.gitkeep b/packages/server/src/system/seeds/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/server/src/system/seeds/seed_subscriptions_plans.js b/packages/server/src/system/seeds/seed_subscriptions_plans.js deleted file mode 100644 index 0e69b94db..000000000 --- a/packages/server/src/system/seeds/seed_subscriptions_plans.js +++ /dev/null @@ -1,66 +0,0 @@ - -exports.seed = (knex) => { - // Deletes ALL existing entries - return knex('subscription_plans').del() - .then(() => { - // Inserts seed entries - return knex('subscription_plans').insert([ - { - name: 'Essentials', - slug: 'essentials-monthly', - price: 100, - active: true, - currency: 'LYD', - trial_period: 7, - trial_interval: 'days', - }, - { - name: 'Essentials', - slug: 'essentials-yearly', - price: 1200, - active: true, - currency: 'LYD', - trial_period: 12, - trial_interval: 'months', - }, - { - name: 'Pro', - slug: 'pro-monthly', - price: 200, - active: true, - currency: 'LYD', - trial_period: 1, - trial_interval: 'months', - }, - { - name: 'Pro', - slug: 'pro-yearly', - price: 500, - active: true, - currency: 'LYD', - invoice_period: 12, - invoice_interval: 'month', - index: 2, - }, - { - name: 'Plus', - slug: 'plus-monthly', - price: 200, - active: true, - currency: 'LYD', - trial_period: 1, - trial_interval: 'months', - }, - { - name: 'Plus', - slug: 'plus-yearly', - price: 500, - active: true, - currency: 'LYD', - invoice_period: 12, - invoice_interval: 'month', - index: 2, - }, - ]); - }); -}; From 57e3f68219f1444f378c13687d63d732926921e7 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Thu, 2 Mar 2023 22:49:46 +0200 Subject: [PATCH 3/9] feat(server): deprecated the subscription module. --- packages/server/src/api/controllers/Setup.ts | 102 ------------------- 1 file changed, 102 deletions(-) delete mode 100644 packages/server/src/api/controllers/Setup.ts diff --git a/packages/server/src/api/controllers/Setup.ts b/packages/server/src/api/controllers/Setup.ts deleted file mode 100644 index 256d0bc47..000000000 --- a/packages/server/src/api/controllers/Setup.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Router, Request, Response, NextFunction } from 'express'; -import { check, ValidationChain } from 'express-validator'; -import BaseController from './BaseController'; -import SetupService from '@/services/Setup/SetupService'; -import { Inject, Service } from 'typedi'; -import { IOrganizationSetupDTO } from '@/interfaces'; -import { ServiceError } from '@/exceptions'; -// Middlewares -import JWTAuth from '@/api/middleware/jwtAuth'; -import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser'; -import SubscriptionMiddleware from '@/api/middleware/SubscriptionMiddleware'; -import TenancyMiddleware from '@/api/middleware/TenancyMiddleware'; -import EnsureTenantIsInitialized from '@/api/middleware/EnsureTenantIsInitialized'; -import SettingsMiddleware from '@/api/middleware/SettingsMiddleware'; - -@Service() -export default class SetupController extends BaseController { - @Inject() - setupService: SetupService; - - router() { - const router = Router('/setup'); - - router.use(JWTAuth); - router.use(AttachCurrentTenantUser); - router.use(TenancyMiddleware); - router.use(SubscriptionMiddleware('main')); - router.use(EnsureTenantIsInitialized); - router.use(SettingsMiddleware); - router.post( - '/organization', - this.organizationSetupSchema, - this.validationResult, - this.asyncMiddleware(this.organizationSetup.bind(this)), - this.handleServiceErrors - ); - return router; - } - - /** - * Organization setup schema. - */ - private get organizationSetupSchema(): ValidationChain[] { - return [ - check('organization_name').exists().trim(), - check('base_currency').exists(), - check('time_zone').exists(), - check('fiscal_year').exists(), - check('industry').optional(), - ]; - } - - /** - * Organization setup. - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - * @returns - */ - async organizationSetup(req: Request, res: Response, next: NextFunction) { - const { tenantId } = req; - const setupDTO: IOrganizationSetupDTO = this.matchedBodyData(req); - - try { - await this.setupService.organizationSetup(tenantId, setupDTO); - - return res.status(200).send({ - message: 'The setup settings set successfully.', - }); - } catch (error) { - next(error); - } - } - - /** - * Handles service errors. - * @param {Error} error - * @param {Request} req - * @param {Response} res - * @param {NextFunction} next - */ - handleServiceErrors( - error: Error, - req: Request, - res: Response, - next: NextFunction - ) { - if (error instanceof ServiceError) { - if (error.errorType === 'TENANT_IS_ALREADY_SETUPED') { - return res.status(400).send({ - errors: [{ type: 'TENANT_IS_ALREADY_SETUPED', code: 1000 }], - }); - } - if (error.errorType === 'BASE_CURRENCY_INVALID') { - return res.status(400).send({ - errors: [{ type: 'BASE_CURRENCY_INVALID', code: 110 }], - }); - } - } - next(error); - } -} From 0c1bf302e5b85e3b2ea62f9e8046419981c2a997 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Sat, 4 Mar 2023 23:08:02 +0200 Subject: [PATCH 4/9] feat(webapp): deprecate the subscription step in onboarding process --- packages/webapp/src/constants/registerWizard.tsx | 3 --- .../webapp/src/containers/Setup/SetupRightSection.tsx | 9 --------- .../webapp/src/containers/Setup/SetupWizardContent.tsx | 3 +-- .../webapp/src/containers/Setup/WizardSetupSteps.tsx | 1 + packages/webapp/src/hooks/query/organization.tsx | 8 ++------ .../webapp/src/store/organizations/withSetupWizard.tsx | 3 --- 6 files changed, 4 insertions(+), 23 deletions(-) diff --git a/packages/webapp/src/constants/registerWizard.tsx b/packages/webapp/src/constants/registerWizard.tsx index f1d971d2e..e901d5202 100644 --- a/packages/webapp/src/constants/registerWizard.tsx +++ b/packages/webapp/src/constants/registerWizard.tsx @@ -2,9 +2,6 @@ import intl from 'react-intl-universal'; export const getSetupWizardSteps = () => [ - { - label: intl.get('setup.plan.plans'), - }, { label: intl.get('setup.plan.getting_started'), }, diff --git a/packages/webapp/src/containers/Setup/SetupRightSection.tsx b/packages/webapp/src/containers/Setup/SetupRightSection.tsx index 91ee8fa1e..c382b2586 100644 --- a/packages/webapp/src/containers/Setup/SetupRightSection.tsx +++ b/packages/webapp/src/containers/Setup/SetupRightSection.tsx @@ -23,9 +23,6 @@ function SetupRightSection({ // #withSetupWizard setupStepId, setupStepIndex, - - // #withSubscriptions - isSubscriptionActive, }) { return (
@@ -57,12 +54,6 @@ export default compose( isOrganizationBuildRunning, }), ), - withSubscriptions( - ({ isSubscriptionActive }) => ({ - isSubscriptionActive, - }), - 'main', - ), withSetupWizard(({ setupStepId, setupStepIndex }) => ({ setupStepId, setupStepIndex, diff --git a/packages/webapp/src/containers/Setup/SetupWizardContent.tsx b/packages/webapp/src/containers/Setup/SetupWizardContent.tsx index 57b162ccf..603aa395e 100644 --- a/packages/webapp/src/containers/Setup/SetupWizardContent.tsx +++ b/packages/webapp/src/containers/Setup/SetupWizardContent.tsx @@ -4,7 +4,7 @@ import React from 'react'; import SetupSteps from './SetupSteps'; import WizardSetupSteps from './WizardSetupSteps'; -import SetupSubscription from './SetupSubscription'; +// import SetupSubscription from './SetupSubscription'; import SetupOrganizationPage from './SetupOrganizationPage'; import SetupInitializingForm from './SetupInitializingForm'; import SetupCongratsPage from './SetupCongratsPage'; @@ -19,7 +19,6 @@ export default function SetupWizardContent({ setupStepIndex, setupStepId }) {
- diff --git a/packages/webapp/src/containers/Setup/WizardSetupSteps.tsx b/packages/webapp/src/containers/Setup/WizardSetupSteps.tsx index 36b53a37e..0381d11d9 100644 --- a/packages/webapp/src/containers/Setup/WizardSetupSteps.tsx +++ b/packages/webapp/src/containers/Setup/WizardSetupSteps.tsx @@ -25,6 +25,7 @@ export default function WizardSetupSteps({ currentStep = 1 }) { ))} diff --git a/packages/webapp/src/hooks/query/organization.tsx b/packages/webapp/src/hooks/query/organization.tsx index aea3ab96c..3945de70f 100644 --- a/packages/webapp/src/hooks/query/organization.tsx +++ b/packages/webapp/src/hooks/query/organization.tsx @@ -1,11 +1,11 @@ // @ts-nocheck +import { omit } from 'lodash'; import { useMutation, useQueryClient } from 'react-query'; import { batch } from 'react-redux'; import t from './types'; import useApiRequest from '../useRequest'; import { useRequestQuery } from '../useQueryRequest'; -import { useSetOrganizations, useSetSubscriptions } from '../state'; -import { omit } from 'lodash'; +import { useSetOrganizations } from '../state'; /** * Retrieve organizations of the authenticated user. @@ -32,7 +32,6 @@ export function useOrganizations(props) { */ export function useCurrentOrganization(props) { const setOrganizations = useSetOrganizations(); - const setSubscriptions = useSetSubscriptions(); return useRequestQuery( [t.ORGANIZATION_CURRENT], @@ -44,9 +43,6 @@ export function useCurrentOrganization(props) { const organization = omit(data, ['subscriptions']); batch(() => { - // Sets subscriptions. - setSubscriptions(data.subscriptions); - // Sets organizations. setOrganizations([organization]); }); diff --git a/packages/webapp/src/store/organizations/withSetupWizard.tsx b/packages/webapp/src/store/organizations/withSetupWizard.tsx index 7f39a2466..1a0b27f5b 100644 --- a/packages/webapp/src/store/organizations/withSetupWizard.tsx +++ b/packages/webapp/src/store/organizations/withSetupWizard.tsx @@ -6,18 +6,15 @@ export default (mapState) => { const { isOrganizationSetupCompleted, isOrganizationReady, - isSubscriptionActive, isOrganizationBuildRunning } = props; const condits = { isCongratsStep: isOrganizationSetupCompleted, - isSubscriptionStep: !isSubscriptionActive, isInitializingStep: isOrganizationBuildRunning, isOrganizationStep: !isOrganizationReady && !isOrganizationBuildRunning, }; const scenarios = [ - { condition: condits.isSubscriptionStep, step: 'subscription' }, { condition: condits.isOrganizationStep, step: 'organization' }, { condition: condits.isInitializingStep, step: 'initializing' }, { condition: condits.isCongratsStep, step: 'congrats' }, From f26ced97fec0cfe293789e1b881d494228696870 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Sun, 5 Mar 2023 13:21:06 +0200 Subject: [PATCH 5/9] feat(webapp): deprecate the subscription from webapp. --- .../src/components/Dashboard/Dashboard.tsx | 11 +- .../components/Dashboard/DashboardBoot.tsx | 10 +- .../Dashboard/DashboardBreadcrumbs.tsx | 39 +++---- .../components/Dashboard/DashboardContent.tsx | 2 +- .../Dashboard/DashboardContentRoute.tsx | 19 +--- .../{ => DashboardTopbar}/DashboardTopbar.tsx | 105 +++--------------- .../Dashboard/DashboardTopbar/_components.tsx | 61 ++++++++++ .../Dashboard/DashboardTopbar/index.ts | 1 + .../src/components/Dashboard/TopbarUser.tsx | 32 ++---- packages/webapp/src/constants/sidebarMenu.tsx | 16 --- .../Dashboard/Sidebar/SidebarContainer.tsx | 9 -- .../Dashboard/Sidebar/SidebarMenu.tsx | 12 +- .../containers/Dashboard/Sidebar/hooks.tsx | 5 +- .../containers/Setup/SetupRightSection.tsx | 1 - packages/webapp/src/routes/register.tsx | 23 ---- 15 files changed, 118 insertions(+), 228 deletions(-) rename packages/webapp/src/components/Dashboard/{ => DashboardTopbar}/DashboardTopbar.tsx (57%) create mode 100644 packages/webapp/src/components/Dashboard/DashboardTopbar/_components.tsx create mode 100644 packages/webapp/src/components/Dashboard/DashboardTopbar/index.ts delete mode 100644 packages/webapp/src/routes/register.tsx diff --git a/packages/webapp/src/components/Dashboard/Dashboard.tsx b/packages/webapp/src/components/Dashboard/Dashboard.tsx index 8371e0fba..2637128cf 100644 --- a/packages/webapp/src/components/Dashboard/Dashboard.tsx +++ b/packages/webapp/src/components/Dashboard/Dashboard.tsx @@ -14,19 +14,16 @@ import GlobalHotkeys from './GlobalHotkeys'; import DashboardProvider from './DashboardProvider'; import DrawersContainer from '@/components/DrawersContainer'; import AlertsContainer from '@/containers/AlertsContainer'; -import EnsureSubscriptionIsActive from '../Guards/EnsureSubscriptionIsActive'; /** * Dashboard preferences. */ function DashboardPreferences() { return ( - - - - - - + + + + ); } diff --git a/packages/webapp/src/components/Dashboard/DashboardBoot.tsx b/packages/webapp/src/components/Dashboard/DashboardBoot.tsx index a3316a7f5..2389cc4a7 100644 --- a/packages/webapp/src/components/Dashboard/DashboardBoot.tsx +++ b/packages/webapp/src/components/Dashboard/DashboardBoot.tsx @@ -7,26 +7,20 @@ import { } from '@/hooks/query'; import { useSplashLoading } from '@/hooks/state'; import { useWatch, useWatchImmediate, useWhen } from '@/hooks'; -import { useSubscription } from '@/hooks/state'; import { setCookie, getCookie } from '@/utils'; /** * Dashboard meta async booting. - * - Fetches the dashboard meta only if the organization subscribe is active. - * - Once the dashboard meta query is loading display dashboard splash screen. + * - Fetches the dashboard meta in booting state. + * - Once the dashboard meta query started loading display dashboard splash screen. */ export function useDashboardMetaBoot() { - const { isSubscriptionActive } = useSubscription(); - const { data: dashboardMeta, isLoading: isDashboardMetaLoading, isSuccess: isDashboardMetaSuccess, } = useDashboardMeta({ keepPreviousData: true, - - // Avoid run the query if the organization subscription is not active. - enabled: isSubscriptionActive, }); const [startLoading, stopLoading] = useSplashLoading(); diff --git a/packages/webapp/src/components/Dashboard/DashboardBreadcrumbs.tsx b/packages/webapp/src/components/Dashboard/DashboardBreadcrumbs.tsx index 38537b2a4..d04144e8e 100644 --- a/packages/webapp/src/components/Dashboard/DashboardBreadcrumbs.tsx +++ b/packages/webapp/src/components/Dashboard/DashboardBreadcrumbs.tsx @@ -1,5 +1,6 @@ // @ts-nocheck import React from 'react'; +import { useHistory } from 'react-router-dom'; import { CollapsibleList, MenuItem, @@ -7,29 +8,29 @@ import { Boundary, } from '@blueprintjs/core'; import withBreadcrumbs from 'react-router-breadcrumbs-hoc'; -import { getDashboardRoutes } from '@/routes/dashboard'; -import { useHistory } from 'react-router-dom'; -function DashboardBreadcrumbs({ breadcrumbs }){ +function DashboardBreadcrumbs({ breadcrumbs }) { const history = useHistory(); - return( + return ( } - collapseFrom={Boundary.START} - visibleItemCount={0}> - { - breadcrumbs.map(({ breadcrumb,match })=>{ - return ( history.push(match.url) } />) - }) - } + className={Classes.BREADCRUMBS} + dropdownTarget={} + collapseFrom={Boundary.START} + visibleItemCount={0} + > + {breadcrumbs.map(({ breadcrumb, match }) => { + return ( + history.push(match.url)} + /> + ); + })} - ) + ); } -export default withBreadcrumbs([])(DashboardBreadcrumbs) +export default withBreadcrumbs([])(DashboardBreadcrumbs); diff --git a/packages/webapp/src/components/Dashboard/DashboardContent.tsx b/packages/webapp/src/components/Dashboard/DashboardContent.tsx index fcdce6195..640bce34b 100644 --- a/packages/webapp/src/components/Dashboard/DashboardContent.tsx +++ b/packages/webapp/src/components/Dashboard/DashboardContent.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import React from 'react'; import { ErrorBoundary } from 'react-error-boundary'; -import DashboardTopbar from '@/components/Dashboard/DashboardTopbar'; +import DashboardTopbar from '@/components/Dashboard/DashboardTopbar/DashboardTopbar'; import DashboardContentRoutes from '@/components/Dashboard/DashboardContentRoute'; import DashboardErrorBoundary from './DashboardErrorBoundary'; diff --git a/packages/webapp/src/components/Dashboard/DashboardContentRoute.tsx b/packages/webapp/src/components/Dashboard/DashboardContentRoute.tsx index cf5ccf4b5..ac23d319a 100644 --- a/packages/webapp/src/components/Dashboard/DashboardContentRoute.tsx +++ b/packages/webapp/src/components/Dashboard/DashboardContentRoute.tsx @@ -3,15 +3,13 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { getDashboardRoutes } from '@/routes/dashboard'; -import EnsureSubscriptionsIsActive from '../Guards/EnsureSubscriptionsIsActive'; -import EnsureSubscriptionsIsInactive from '../Guards/EnsureSubscriptionsIsInactive'; import DashboardPage from './DashboardPage'; /** * Dashboard inner route content. */ function DashboardContentRouteContent({ route }) { - const content = ( + return ( ); - return route.subscriptionActive ? ( - - ) : route.subscriptionInactive ? ( - - ) : ( - content - ); } /** diff --git a/packages/webapp/src/components/Dashboard/DashboardTopbar.tsx b/packages/webapp/src/components/Dashboard/DashboardTopbar/DashboardTopbar.tsx similarity index 57% rename from packages/webapp/src/components/Dashboard/DashboardTopbar.tsx rename to packages/webapp/src/components/Dashboard/DashboardTopbar/DashboardTopbar.tsx index a7422845e..c9e8e6946 100644 --- a/packages/webapp/src/components/Dashboard/DashboardTopbar.tsx +++ b/packages/webapp/src/components/Dashboard/DashboardTopbar/DashboardTopbar.tsx @@ -10,57 +10,19 @@ import { Tooltip, Position, } from '@blueprintjs/core'; -import { FormattedMessage as T } from '@/components'; +import { FormattedMessage as T, Icon, Hint, If } from '@/components'; import DashboardTopbarUser from '@/components/Dashboard/TopbarUser'; import DashboardBreadcrumbs from '@/components/Dashboard/DashboardBreadcrumbs'; import DashboardBackLink from '@/components/Dashboard/DashboardBackLink'; -import { Icon, Hint, If } from '@/components'; import withUniversalSearchActions from '@/containers/UniversalSearch/withUniversalSearchActions'; import withDashboardActions from '@/containers/Dashboard/withDashboardActions'; import withDashboard from '@/containers/Dashboard/withDashboard'; import QuickNewDropdown from '@/containers/QuickNewDropdown/QuickNewDropdown'; +import { DashboardHamburgerButton, DashboardQuickSearchButton } from './_components'; import { compose } from '@/utils'; -import withSubscriptions from '@/containers/Subscriptions/withSubscriptions'; -import { useGetUniversalSearchTypeOptions } from '@/containers/UniversalSearch/utils'; - -function DashboardTopbarSubscriptionMessage() { - return ( -
- - - -
- ); -} - -function DashboardHamburgerButton({ ...props }) { - return ( -