mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
make manual recharge work
This commit is contained in:
@@ -15,45 +15,89 @@ import Exception from 'Common/Types/Exception/Exception';
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
router.post('/notification/recharge', UserMiddleware.getUserMiddleware, async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
router.post(
|
||||
'/notification/recharge',
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
try {
|
||||
const amount: number = req.body.amount;
|
||||
const projectId: ObjectID = JSONFunctions.deserializeValue(
|
||||
req.body.projectId
|
||||
) as ObjectID;
|
||||
|
||||
try {
|
||||
const amount: number = req.body.amount;
|
||||
const projectId: ObjectID = JSONFunctions.deserializeValue(req.body.projectId) as ObjectID;
|
||||
if (!amount || typeof amount !== 'number') {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid amount')
|
||||
);
|
||||
}
|
||||
|
||||
if (!amount || typeof amount !== 'number') {
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('Invalid amount'));
|
||||
if (amount > 1000) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Amount cannot be greater than 1000')
|
||||
);
|
||||
}
|
||||
|
||||
if (amount < 20) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Amount cannot be less than 20')
|
||||
);
|
||||
}
|
||||
|
||||
if (!projectId || !projectId.toString()) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Invalid projectId')
|
||||
);
|
||||
}
|
||||
|
||||
// get permissions. if user has permission to recharge, then recharge
|
||||
|
||||
if (
|
||||
!(req as OneUptimeRequest).userTenantAccessPermission ||
|
||||
!(req as OneUptimeRequest).userTenantAccessPermission![
|
||||
projectId.toString()
|
||||
]
|
||||
) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException('Permission for this user not found')
|
||||
);
|
||||
}
|
||||
|
||||
const permissions: Array<Permission> = (
|
||||
req as OneUptimeRequest
|
||||
).userTenantAccessPermission![
|
||||
projectId.toString()
|
||||
]!.permissions.map((permission: UserPermission) => {
|
||||
return permission.permission;
|
||||
});
|
||||
|
||||
if (
|
||||
permissions.includes(Permission.ProjectOwner) ||
|
||||
permissions.includes(Permission.CanManageProjectBilling)
|
||||
) {
|
||||
await NotificationService.rechargeBalance(projectId, amount);
|
||||
} else {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadDataException(
|
||||
'User does not have permission to recharge. You need any one of these permissions - ProjectOwner, CanManageProjectBilling'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
return Response.sendErrorResponse(req, res, err as Exception);
|
||||
}
|
||||
|
||||
if(amount > 1000) {
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('Amount cannot be greater than 1000'));
|
||||
}
|
||||
|
||||
if(amount < 20){
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('Amount cannot be less than 20'));
|
||||
}
|
||||
|
||||
if (!projectId || !projectId.toString()) {
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('Invalid projectId'));
|
||||
}
|
||||
|
||||
|
||||
// get permissions. if user has permission to recharge, then recharge
|
||||
|
||||
if (!(req as OneUptimeRequest).userTenantAccessPermission || !(req as OneUptimeRequest).userTenantAccessPermission![projectId.toString()]) {
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('Permission for this user not found'));
|
||||
}
|
||||
|
||||
const permissions = (req as OneUptimeRequest).userTenantAccessPermission![projectId.toString()]!.permissions.map((permission: UserPermission) => permission.permission);
|
||||
|
||||
if (permissions.includes(Permission.ProjectOwner) || permissions.includes(Permission.CanManageProjectBilling)) {
|
||||
await NotificationService.rechargeBalance(projectId, amount)
|
||||
} else {
|
||||
return Response.sendErrorResponse(req, res, new BadDataException('User does not have permission to recharge. You need any one of these permissions - ProjectOwner, CanManageProjectBilling'));
|
||||
}
|
||||
} catch (err: any) {
|
||||
return Response.sendErrorResponse(req, res, err as Exception);
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -7,8 +7,10 @@ import logger from '../Utils/Logger';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
|
||||
export default class NotificationService {
|
||||
|
||||
public static async rechargeBalance(projectId: ObjectID, amountInUSD: number): Promise<number> {
|
||||
public static async rechargeBalance(
|
||||
projectId: ObjectID,
|
||||
amountInUSD: number
|
||||
): Promise<number> {
|
||||
const project: Project | null = await ProjectService.findOneById({
|
||||
id: projectId,
|
||||
select: {
|
||||
@@ -52,8 +54,9 @@ export default class NotificationService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'ACTION REQUIRED: SMS and Call Recharge Failed for project - ' +
|
||||
(project.name || ''),
|
||||
`We have tried to recharge your SMS and Call balance for project - ${project.name || ''
|
||||
(project.name || ''),
|
||||
`We have tried to recharge your SMS and Call balance for project - ${
|
||||
project.name || ''
|
||||
} and failed. We could not find a payment method for the project. Please add a payment method in project settings.`
|
||||
);
|
||||
}
|
||||
@@ -62,11 +65,10 @@ export default class NotificationService {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// recharge balance
|
||||
const updatedAmount: number = Math.floor(
|
||||
(project.smsOrCallCurrentBalanceInUSDCents || 0) +
|
||||
amountInUSD * 100
|
||||
amountInUSD * 100
|
||||
);
|
||||
|
||||
// If the recharge is succcessful, then update the project balance.
|
||||
@@ -78,12 +80,10 @@ export default class NotificationService {
|
||||
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
smsOrCallCurrentBalanceInUSDCents:
|
||||
updatedAmount,
|
||||
smsOrCallCurrentBalanceInUSDCents: updatedAmount,
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners:
|
||||
false, // reset this flag
|
||||
lowCallAndSMSBalanceNotificationSentToOwners:
|
||||
false, // reset this flag
|
||||
lowCallAndSMSBalanceNotificationSentToOwners: false, // reset this flag
|
||||
notEnabledSmsNotificationSentToOwners: false,
|
||||
},
|
||||
id: project.id!,
|
||||
@@ -95,21 +95,21 @@ export default class NotificationService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'SMS and Call Recharge Successful for project - ' +
|
||||
(project.name || ''),
|
||||
`We have successfully recharged your SMS and Call balance for project - ${project.name || ''
|
||||
} by ${amountInUSD} USD. Your current balance is ${updatedAmount / 100
|
||||
(project.name || ''),
|
||||
`We have successfully recharged your SMS and Call balance for project - ${
|
||||
project.name || ''
|
||||
} by ${amountInUSD} USD. Your current balance is ${
|
||||
updatedAmount / 100
|
||||
} USD.`
|
||||
);
|
||||
|
||||
project.smsOrCallCurrentBalanceInUSDCents =
|
||||
updatedAmount;
|
||||
project.smsOrCallCurrentBalanceInUSDCents = updatedAmount;
|
||||
|
||||
return updatedAmount;
|
||||
} catch (err) {
|
||||
await ProjectService.updateOneById({
|
||||
data: {
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners:
|
||||
true,
|
||||
failedCallAndSMSBalanceChargeNotificationSentToOwners: true,
|
||||
},
|
||||
id: project.id!,
|
||||
props: {
|
||||
@@ -119,8 +119,9 @@ export default class NotificationService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'ACTION REQUIRED: SMS and Call Recharge Failed for project - ' +
|
||||
(project.name || ''),
|
||||
`We have tried recharged your SMS and Call balance for project - ${project.name || ''
|
||||
(project.name || ''),
|
||||
`We have tried recharged your SMS and Call balance for project - ${
|
||||
project.name || ''
|
||||
} and failed. Please make sure your payment method is upto date and has sufficient balance. You can add new payment methods in project settings.`
|
||||
);
|
||||
logger.error(err);
|
||||
@@ -166,7 +167,6 @@ export default class NotificationService {
|
||||
? options.enableAutoRechargeSmsOrCallBalance
|
||||
: project?.enableAutoRechargeSmsOrCallBalance || false;
|
||||
|
||||
|
||||
if (!project) {
|
||||
return 0;
|
||||
}
|
||||
@@ -180,11 +180,11 @@ export default class NotificationService {
|
||||
(project.smsOrCallCurrentBalanceInUSDCents || 0) / 100 <
|
||||
autoRechargeSmsOrCallWhenCurrentBalanceFallsInUSD
|
||||
) {
|
||||
|
||||
const updatedAmount: number = await this.rechargeBalance(projectId, autoRechargeSmsOrCallByBalanceInUSD);
|
||||
project.smsOrCallCurrentBalanceInUSDCents =
|
||||
updatedAmount;
|
||||
|
||||
const updatedAmount: number = await this.rechargeBalance(
|
||||
projectId,
|
||||
autoRechargeSmsOrCallByBalanceInUSD
|
||||
);
|
||||
project.smsOrCallCurrentBalanceInUSDCents = updatedAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,19 +61,17 @@ const CardModelDetail: Function = <TBaseModel extends BaseModel>(
|
||||
let cardButtons: Array<CardButtonSchema> = [];
|
||||
|
||||
if (props.isEditable && hasPermissionToEdit) {
|
||||
cardButtons.push(
|
||||
{
|
||||
title: props.editButtonText || `Edit ${model.singularName}`,
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
onClick: () => {
|
||||
setShowModal(true);
|
||||
},
|
||||
icon: IconProp.Edit,
|
||||
cardButtons.push({
|
||||
title: props.editButtonText || `Edit ${model.singularName}`,
|
||||
buttonStyle: ButtonStyleType.NORMAL,
|
||||
onClick: () => {
|
||||
setShowModal(true);
|
||||
},
|
||||
);
|
||||
icon: IconProp.Edit,
|
||||
});
|
||||
}
|
||||
|
||||
if(props.cardProps.buttons) {
|
||||
if (props.cardProps.buttons) {
|
||||
cardButtons = cardButtons.concat(...props.cardProps.buttons);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,13 @@ import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
const Settings: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
): ReactElement => {
|
||||
|
||||
|
||||
const [showRechargeBalanceModal, setShowRechargeBalanceModal] = useState<boolean>(false);
|
||||
const [isRechargeBalanceLoading, setIsRechargeBalanceLoading] = useState<boolean>(false);
|
||||
const [rechargeBalanceError, setRechargeBalanceError] = useState<string | null>(null);
|
||||
const [showRechargeBalanceModal, setShowRechargeBalanceModal] =
|
||||
useState<boolean>(false);
|
||||
const [isRechargeBalanceLoading, setIsRechargeBalanceLoading] =
|
||||
useState<boolean>(false);
|
||||
const [rechargeBalanceError, setRechargeBalanceError] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
return (
|
||||
<Page
|
||||
@@ -67,9 +69,9 @@ const Settings: FunctionComponent<PageComponentProps> = (
|
||||
icon: IconProp.Add,
|
||||
onClick: () => {
|
||||
setShowRechargeBalanceModal(true);
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
isEditable={false}
|
||||
modelDetailProps={{
|
||||
@@ -322,8 +324,7 @@ const Settings: FunctionComponent<PageComponentProps> = (
|
||||
<></>
|
||||
)}
|
||||
|
||||
|
||||
{showRechargeBalanceModal ?
|
||||
{showRechargeBalanceModal ? (
|
||||
<BasicFormModal
|
||||
title={'Recharge Balance'}
|
||||
onClose={() => {
|
||||
@@ -335,10 +336,16 @@ const Settings: FunctionComponent<PageComponentProps> = (
|
||||
onSubmit={async (item: JSONObject) => {
|
||||
setIsRechargeBalanceLoading(true);
|
||||
try {
|
||||
await API.post(URL.fromString(DASHBOARD_API_URL.toString()).addRoute("/notification/recharge"), {
|
||||
amount: item['amount'],
|
||||
projectId: DashboardNavigation.getProjectId()?.toString(),
|
||||
});
|
||||
await API.post(
|
||||
URL.fromString(
|
||||
DASHBOARD_API_URL.toString()
|
||||
).addRoute('/notification/recharge'),
|
||||
{
|
||||
amount: item['amount'],
|
||||
projectId:
|
||||
DashboardNavigation.getProjectId()?.toString(),
|
||||
}
|
||||
);
|
||||
setIsRechargeBalanceLoading(false);
|
||||
setShowRechargeBalanceModal(false);
|
||||
Navigation.reload();
|
||||
@@ -346,10 +353,8 @@ const Settings: FunctionComponent<PageComponentProps> = (
|
||||
setRechargeBalanceError(API.getFriendlyMessage(e));
|
||||
setIsRechargeBalanceLoading(false);
|
||||
}
|
||||
|
||||
}}
|
||||
formProps={{
|
||||
|
||||
error: rechargeBalanceError || '',
|
||||
fields: [
|
||||
{
|
||||
@@ -366,11 +371,12 @@ const Settings: FunctionComponent<PageComponentProps> = (
|
||||
},
|
||||
fieldType: FormFieldSchemaType.PositveNumber,
|
||||
},
|
||||
|
||||
],
|
||||
}}
|
||||
/> : <></>}
|
||||
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ export default class SmsService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'SMS notifications not enabled for ' +
|
||||
(project.name || ''),
|
||||
(project.name || ''),
|
||||
`We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/> <br/> This SMS was not sent because SMS notifications are not enabled for this project. Please enable SMS notifications in project settings.`
|
||||
);
|
||||
}
|
||||
@@ -114,11 +114,13 @@ export default class SmsService {
|
||||
}
|
||||
|
||||
// check if auto recharge is enabled and current balance is low.
|
||||
let updatedBalance: number = project.smsOrCallCurrentBalanceInUSDCents!;
|
||||
let updatedBalance: number =
|
||||
project.smsOrCallCurrentBalanceInUSDCents!;
|
||||
try {
|
||||
updatedBalance = await NotificationService.rechargeIfBalanceIsLow(
|
||||
project.id!
|
||||
);
|
||||
updatedBalance =
|
||||
await NotificationService.rechargeIfBalanceIsLow(
|
||||
project.id!
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
}
|
||||
@@ -149,8 +151,9 @@ export default class SmsService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'Low SMS and Call Balance for ' +
|
||||
(project.name || ''),
|
||||
`We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/>This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${project.smsOrCallCurrentBalanceInUSDCents || 0
|
||||
(project.name || ''),
|
||||
`We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/>This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${
|
||||
project.smsOrCallCurrentBalanceInUSDCents || 0
|
||||
} USD cents. Required balance to send this SMS should is ${SMSDefaultCostInCents} USD cents. Please enable auto recharge or recharge manually.`
|
||||
);
|
||||
}
|
||||
@@ -183,8 +186,9 @@ export default class SmsService {
|
||||
await ProjectService.sendEmailToProjectOwners(
|
||||
project.id!,
|
||||
'Low SMS and Call Balance for ' +
|
||||
(project.name || ''),
|
||||
`We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/> <br/> This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${project.smsOrCallCurrentBalanceInUSDCents
|
||||
(project.name || ''),
|
||||
`We tried to send an SMS to ${to.toString()} with message: <br/> <br/> ${message} <br/> <br/> This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${
|
||||
project.smsOrCallCurrentBalanceInUSDCents
|
||||
} cents. Required balance is ${SMSDefaultCostInCents} cents to send this SMS. Please enable auto recharge or recharge manually.`
|
||||
);
|
||||
}
|
||||
@@ -210,7 +214,7 @@ export default class SmsService {
|
||||
|
||||
project.smsOrCallCurrentBalanceInUSDCents = Math.floor(
|
||||
project.smsOrCallCurrentBalanceInUSDCents! -
|
||||
SMSDefaultCostInCents
|
||||
SMSDefaultCostInCents
|
||||
);
|
||||
|
||||
await ProjectService.updateOneById({
|
||||
|
||||
Reference in New Issue
Block a user