From 44fc26b156b219a49cd95c4800a3ab2130e6a92e Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Thu, 2 Mar 2023 21:34:06 +0200 Subject: [PATCH] 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'); -};