mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
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:
34
backend/src/ee/controllers/v1/cloudProductsController.ts
Normal file
34
backend/src/ee/controllers/v1/cloudProductsController.ts
Normal 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: []
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import * as organizationsController from './organizationsController';
|
|||||||
import * as workspaceController from './workspaceController';
|
import * as workspaceController from './workspaceController';
|
||||||
import * as actionController from './actionController';
|
import * as actionController from './actionController';
|
||||||
import * as membershipController from './membershipController';
|
import * as membershipController from './membershipController';
|
||||||
|
import * as cloudProductsController from './cloudProductsController';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
stripeController,
|
stripeController,
|
||||||
@@ -13,5 +14,6 @@ export {
|
|||||||
organizationsController,
|
organizationsController,
|
||||||
workspaceController,
|
workspaceController,
|
||||||
actionController,
|
actionController,
|
||||||
membershipController
|
membershipController,
|
||||||
|
cloudProductsController
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,82 @@
|
|||||||
import { Types } from 'mongoose';
|
import * as Sentry from '@sentry/node';
|
||||||
import { Request, Response } from 'express';
|
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) => {
|
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({
|
const cachedPlan = EELicenseService.localFeatureSet.get(req.organization._id.toString());
|
||||||
organizationId: new Types.ObjectId(organizationId)
|
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({
|
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);
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
backend/src/ee/routes/v1/cloudProducts.ts
Normal file
20
backend/src/ee/routes/v1/cloudProducts.ts
Normal 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;
|
||||||
@@ -3,11 +3,13 @@ import secretSnapshot from './secretSnapshot';
|
|||||||
import organizations from './organizations';
|
import organizations from './organizations';
|
||||||
import workspace from './workspace';
|
import workspace from './workspace';
|
||||||
import action from './action';
|
import action from './action';
|
||||||
|
import cloudProducts from './cloudProducts';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
secret,
|
secret,
|
||||||
secretSnapshot,
|
secretSnapshot,
|
||||||
organizations,
|
organizations,
|
||||||
workspace,
|
workspace,
|
||||||
action
|
action,
|
||||||
|
cloudProducts
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
requireOrganizationAuth,
|
requireOrganizationAuth,
|
||||||
validateRequest
|
validateRequest
|
||||||
} from '../../../middleware';
|
} from '../../../middleware';
|
||||||
import { param } from 'express-validator';
|
import { param, body } from 'express-validator';
|
||||||
import { organizationsController } from '../../controllers/v1';
|
import { organizationsController } from '../../controllers/v1';
|
||||||
import {
|
import {
|
||||||
OWNER, ADMIN, MEMBER, ACCEPTED
|
OWNER, ADMIN, MEMBER, ACCEPTED
|
||||||
@@ -25,4 +25,48 @@ router.get(
|
|||||||
organizationsController.getOrganizationPlan
|
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;
|
export default router;
|
||||||
@@ -26,7 +26,8 @@ import {
|
|||||||
secret as eeSecretRouter,
|
secret as eeSecretRouter,
|
||||||
secretSnapshot as eeSecretSnapshotRouter,
|
secretSnapshot as eeSecretSnapshotRouter,
|
||||||
action as eeActionRouter,
|
action as eeActionRouter,
|
||||||
organizations as eeOrganizationsRouter
|
organizations as eeOrganizationsRouter,
|
||||||
|
cloudProducts as eeCloudProductsRouter
|
||||||
} from './ee/routes/v1';
|
} from './ee/routes/v1';
|
||||||
import {
|
import {
|
||||||
signup as v1SignupRouter,
|
signup as v1SignupRouter,
|
||||||
@@ -122,6 +123,7 @@ const main = async () => {
|
|||||||
app.use('/api/v1/workspace', eeWorkspaceRouter);
|
app.use('/api/v1/workspace', eeWorkspaceRouter);
|
||||||
app.use('/api/v1/action', eeActionRouter);
|
app.use('/api/v1/action', eeActionRouter);
|
||||||
app.use('/api/v1/organizations', eeOrganizationsRouter);
|
app.use('/api/v1/organizations', eeOrganizationsRouter);
|
||||||
|
app.use('/api/v1/cloud-products', eeCloudProductsRouter);
|
||||||
|
|
||||||
// v1 routes (default)
|
// v1 routes (default)
|
||||||
app.use('/api/v1/signup', v1SignupRouter);
|
app.use('/api/v1/signup', v1SignupRouter);
|
||||||
|
|||||||
Reference in New Issue
Block a user