Merge pull request #597 from Infisical/connect-to-license-server

Added add/remove/get organization payment methods and get cloud plans…
This commit is contained in:
BlackMagiq
2023-05-23 22:39:35 +03:00
committed by GitHub
8 changed files with 182 additions and 50 deletions

View File

@@ -0,0 +1,34 @@
import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import { EELicenseService } from '../../services';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
/**
* Return available cloud product information.
* Note: Nicely formatted to easily construct a table from
* @param req
* @param res
* @returns
*/
export const getCloudProducts = async (req: Request, res: Response) => {
try {
const billingCycle = req.query['billing-cycle'] as string;
if (EELicenseService.instanceType === 'cloud') {
const { data } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
);
return res.status(200).send(data);
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
}
return res.status(200).send({
head: [],
rows: []
});
}

View File

@@ -5,6 +5,7 @@ import * as organizationsController from './organizationsController';
import * as workspaceController from './workspaceController';
import * as actionController from './actionController';
import * as membershipController from './membershipController';
import * as cloudProductsController from './cloudProductsController';
export {
stripeController,
@@ -13,5 +14,6 @@ export {
organizationsController,
workspaceController,
actionController,
membershipController
membershipController,
cloudProductsController
}

View File

@@ -1,15 +1,82 @@
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import { Request, Response } from 'express';
import { getOrganizationPlanHelper } from '../../helpers/organizations';
import { getLicenseServerUrl } from '../../../config';
import { licenseServerKeyRequest } from '../../../config/request';
import { EELicenseService } from '../../services';
/**
* Return the organization's current plan and allowed feature set
*/
export const getOrganizationPlan = async (req: Request, res: Response) => {
const { organizationId } = req.params;
try {
if (EELicenseService.instanceType === 'cloud') {
// instance of Infisical is a cloud instance
const plan = await getOrganizationPlanHelper({
organizationId: new Types.ObjectId(organizationId)
const cachedPlan = EELicenseService.localFeatureSet.get(req.organization._id.toString());
if (cachedPlan) return cachedPlan;
const { data: { currentPlan } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`
);
// cache fetched plan for organization
EELicenseService.localFeatureSet.set(req.organization._id.toString(), currentPlan);
return res.status(200).send({
plan: currentPlan
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
}
return res.status(200).send({
plan: EELicenseService.globalFeatureSet
});
}
/**
* Return the organization's payment methods on file
*/
export const getOrganizationPmtMethods = async (req: Request, res: Response) => {
const { data: { pmtMethods } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`
);
return res.status(200).send({
pmtMethods
});
}
/**
* Return a Stripe session URL to add payment method for organization
*/
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
const {
success_url,
cancel_url
} = req.body;
const { data: { url } } = await licenseServerKeyRequest.post(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
{
success_url,
cancel_url
}
);
return res.status(200).send({
plan
});
url
});
}
export const deleteOrganizationPmtMethod = async (req: Request, res: Response) => {
const { pmtMethodId } = req.params;
const { data } = await licenseServerKeyRequest.delete(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods/${pmtMethodId}`,
);
return res.status(200).send(data);
}

View File

@@ -1,39 +0,0 @@
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import { Organization } from '../../models';
import { EELicenseService } from '../services';
import { getLicenseServerUrl } from '../../config';
import { licenseServerKeyRequest } from '../../config/request';
import { OrganizationNotFoundError } from '../../utils/errors';
export const getOrganizationPlanHelper = async ({
organizationId
}: {
organizationId: Types.ObjectId;
}) => {
try {
if (EELicenseService.instanceType === 'cloud') {
// instance of Infisical is a cloud instance
const organization = await Organization.findById(organizationId);
if (!organization) throw OrganizationNotFoundError();
const cachedPlan = EELicenseService.localFeatureSet.get(organizationId.toString());
if (cachedPlan) return cachedPlan;
const { data: { currentPlan } } = await licenseServerKeyRequest.get(
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`
);
// cache fetched plan for organization
EELicenseService.localFeatureSet.set(organizationId.toString(), currentPlan);
return currentPlan;
}
return EELicenseService.globalFeatureSet;
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return EELicenseService.globalFeatureSet;
}
}

View File

@@ -0,0 +1,20 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
validateRequest
} from '../../../middleware';
import { query } from 'express-validator';
import { cloudProductsController } from '../../controllers/v1';
router.get(
'/',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
query('billing-cycle').exists().isIn(['monthly', 'yearly']),
validateRequest,
cloudProductsController.getCloudProducts
);
export default router;

View File

@@ -3,11 +3,13 @@ import secretSnapshot from './secretSnapshot';
import organizations from './organizations';
import workspace from './workspace';
import action from './action';
import cloudProducts from './cloudProducts';
export {
secret,
secretSnapshot,
organizations,
workspace,
action
action,
cloudProducts
}

View File

@@ -5,7 +5,7 @@ import {
requireOrganizationAuth,
validateRequest
} from '../../../middleware';
import { param } from 'express-validator';
import { param, body } from 'express-validator';
import { organizationsController } from '../../controllers/v1';
import {
OWNER, ADMIN, MEMBER, ACCEPTED
@@ -25,4 +25,48 @@ router.get(
organizationsController.getOrganizationPlan
);
router.get(
'/:organizationId/billing-details/payment-methods',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
validateRequest,
organizationsController.getOrganizationPmtMethods
);
router.post(
'/:organizationId/billing-details/payment-methods',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
body('success_url').exists().isString(),
body('cancel_url').exists().isString(),
validateRequest,
organizationsController.addOrganizationPmtMethod
);
router.delete(
'/:organizationId/billing-details/payment-methods/:pmtMethodId',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireOrganizationAuth({
acceptedRoles: [OWNER, ADMIN, MEMBER],
acceptedStatuses: [ACCEPTED]
}),
param('organizationId').exists().trim(),
validateRequest,
organizationsController.deleteOrganizationPmtMethod
);
export default router;

View File

@@ -26,7 +26,8 @@ import {
secret as eeSecretRouter,
secretSnapshot as eeSecretSnapshotRouter,
action as eeActionRouter,
organizations as eeOrganizationsRouter
organizations as eeOrganizationsRouter,
cloudProducts as eeCloudProductsRouter
} from './ee/routes/v1';
import {
signup as v1SignupRouter,
@@ -122,6 +123,7 @@ const main = async () => {
app.use('/api/v1/workspace', eeWorkspaceRouter);
app.use('/api/v1/action', eeActionRouter);
app.use('/api/v1/organizations', eeOrganizationsRouter);
app.use('/api/v1/cloud-products', eeCloudProductsRouter);
// v1 routes (default)
app.use('/api/v1/signup', v1SignupRouter);