diff --git a/CommonServer/Services/DatabaseService.ts b/CommonServer/Services/DatabaseService.ts index a22b65eafc..6a00d39760 100644 --- a/CommonServer/Services/DatabaseService.ts +++ b/CommonServer/Services/DatabaseService.ts @@ -79,6 +79,26 @@ class DatabaseService { private model!: TBaseModel; private modelName!: string; + + private _hardDeleteItemByColumnName : string = ''; + public get hardDeleteItemByColumnName() : string { + return this._hardDeleteItemByColumnName; + } + public set hardDeleteItemByColumnName(v : string) { + this._hardDeleteItemByColumnName = v; + } + + + + private _hardDeleteItemsOlderThanDays : number = 0; + public get hardDeleteItemsOlderThanDays() : number { + return this._hardDeleteItemsOlderThanDays; + } + public set hardDeleteItemsOlderThanDays(v : number) { + this._hardDeleteItemsOlderThanDays = v; + } + + public constructor( modelType: { new (): TBaseModel }, postgresDatabase?: PostgresDatabase @@ -92,6 +112,11 @@ class DatabaseService { } } + public hardDeleteItemsOlderThanInDays(columnName: string, olderThan: number) { + this.hardDeleteItemByColumnName = columnName; + this.hardDeleteItemsOlderThanDays = olderThan; + } + public getModel(): TBaseModel { return this.model; } diff --git a/CommonServer/Services/Index.ts b/CommonServer/Services/Index.ts index aaa5b6be1a..472f252345 100644 --- a/CommonServer/Services/Index.ts +++ b/CommonServer/Services/Index.ts @@ -68,6 +68,9 @@ import WorkflowService from './WorkflowService'; import WorkflowVariablesService from './WorkflowVariableService'; import WorkflowLogService from './WorkflowLogService'; +// SMS Log Servce +import SmsLogService from './SmsLogService'; + export default [ UserService, ProbeService, @@ -118,4 +121,6 @@ export default [ WorkflowService, WorkflowVariablesService, WorkflowLogService, + + SmsLogService ]; diff --git a/CommonServer/Services/SmsLogService.ts b/CommonServer/Services/SmsLogService.ts index 9ec43b332b..3563f0e300 100644 --- a/CommonServer/Services/SmsLogService.ts +++ b/CommonServer/Services/SmsLogService.ts @@ -5,6 +5,7 @@ import DatabaseService from './DatabaseService'; export class Service extends DatabaseService { public constructor(postgresDatabase?: PostgresDatabase) { super(Model, postgresDatabase); + this.hardDeleteItemsOlderThanInDays("createdAt", 30); } } diff --git a/CommonUI/src/Components/Detail/Detail.tsx b/CommonUI/src/Components/Detail/Detail.tsx index f9fc44aa9d..f4ad2f7b94 100644 --- a/CommonUI/src/Components/Detail/Detail.tsx +++ b/CommonUI/src/Components/Detail/Detail.tsx @@ -74,7 +74,7 @@ const Detail: Function = (props: ComponentProps): ReactElement => { }; const getUSDCentsField: Function = (usdCents: number): ReactElement => { - return
{usdCents / 100} USD
; + return
{usdCents / 100} USD
; }; const getField: Function = (field: Field, index: number): ReactElement => { diff --git a/Dashboard/src/Pages/Settings/CallSms.tsx b/Dashboard/src/Pages/Settings/CallSms.tsx index 7c8ad15a28..9c633ab480 100644 --- a/Dashboard/src/Pages/Settings/CallSms.tsx +++ b/Dashboard/src/Pages/Settings/CallSms.tsx @@ -11,6 +11,7 @@ import PageComponentProps from '../PageComponentProps'; import DashboardSideMenu from './SideMenu'; import FieldType from 'CommonUI/src/Components/Types/FieldType'; import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType'; +import { BILLING_ENABLED } from 'CommonUI/src/Config'; const Settings: FunctionComponent = ( _props: PageComponentProps @@ -41,7 +42,7 @@ const Settings: FunctionComponent = ( sideMenu={} > {/* API Key View */} - = ( fields: [ { field: { - smsOrCallCurrentBalanceInUSD: true, + smsOrCallCurrentBalanceInUSDCents: true, }, - fieldType: FieldType.Number, + fieldType: FieldType.USDCents, title: 'SMS or Call Current Balance', description: 'This is your current balance for SMS or Call. It is in USD. ', @@ -67,7 +68,7 @@ const Settings: FunctionComponent = ( ], modelId: DashboardNavigation.getProjectId()?.toString(), }} - /> + /> : <>} = ( }} /> - = ( ], modelId: DashboardNavigation.getProjectId()?.toString(), }} - /> + /> : <>} ); }; diff --git a/Dashboard/src/Pages/Settings/SideMenu.tsx b/Dashboard/src/Pages/Settings/SideMenu.tsx index 9879d8dfaa..64e6b17a20 100644 --- a/Dashboard/src/Pages/Settings/SideMenu.tsx +++ b/Dashboard/src/Pages/Settings/SideMenu.tsx @@ -155,12 +155,12 @@ const DashboardSideMenu: FunctionComponent = (): ReactElement => { /> = ( _props: PageComponentProps @@ -26,6 +29,70 @@ const SMSLogs: FunctionComponent = ( const [smsModelTitle, setSmsModalTitle] = useState(''); const [smsModelDescription, setSmsModalDescription] = useState(''); + + const modelTableColumns: Columns = [{ + field: { + _id: true, + }, + title: 'Log ID', + type: FieldType.Text, + isFilterable: true, + }, + { + field: { + toNumber: true, + }, + isFilterable: true, + + title: 'To Number', + type: FieldType.Phone, + }, + { + field: { + createdAt: true, + }, + title: 'Sent at', + type: FieldType.DateTime, + isFilterable: true, + }, + + { + field: { + status: true, + }, + title: 'Status', + type: FieldType.Text, + getElement: (item: JSONObject): ReactElement => { + if (item['status']) { + return ( + + ); + } + + return <>; + }, + isFilterable: true, + }]; + + if(BILLING_ENABLED){ + modelTableColumns.push({ + field: { + smsCostInUSDCents: true, + }, + title: 'SMS Cost', + type: FieldType.USDCents, + } as Column); + } + return ( = ( } showRefreshButton={true} showFilterButton={true} - columns={[ - { - field: { - _id: true, - }, - title: 'Log ID', - type: FieldType.Text, - isFilterable: true, - }, - { - field: { - toNumber: true, - }, - isFilterable: true, - - title: 'To Number', - type: FieldType.Phone, - }, - { - field: { - createdAt: true, - }, - title: 'Sent at', - type: FieldType.DateTime, - isFilterable: true, - }, - { - field: { - smsCostInUSDCents: true, - }, - title: 'SMS Cost', - type: FieldType.USDCents, - }, - { - field: { - status: true, - }, - title: 'Status', - type: FieldType.Text, - getElement: (item: JSONObject): ReactElement => { - if (item['status']) { - return ( - - ); - } - - return <>; - }, - isFilterable: true, - }, - ]} + columns={modelTableColumns} /> {showViewSmsTextModal && ( diff --git a/Notification/Services/SmsService.ts b/Notification/Services/SmsService.ts index d488c9c42e..c91cabb4b5 100644 --- a/Notification/Services/SmsService.ts +++ b/Notification/Services/SmsService.ts @@ -155,6 +155,7 @@ export default class SmsService { }); } } catch (e: any) { + smsLog.smsCostInUSDCents = 0; smsLog.status = SmsStatus.Error; smsLog.statusMessage = e && e.message ? e.message.toString() : e.toString(); diff --git a/Workers/Jobs/HardDelete/HardDeleteItemsInDatabase.ts b/Workers/Jobs/HardDelete/HardDeleteItemsInDatabase.ts index 0bc6c56f3c..99d84c5ffb 100644 --- a/Workers/Jobs/HardDelete/HardDeleteItemsInDatabase.ts +++ b/Workers/Jobs/HardDelete/HardDeleteItemsInDatabase.ts @@ -35,3 +35,41 @@ RunCron( } } ); + + +RunCron( + 'HardDelete:HardDeleteOlderItemsInDatabase', + { schedule: IsDevelopment ? EVERY_MINUTE : EVERY_DAY, runOnStartup: false }, + async () => { + for (const service of Services) { + if (service instanceof DatabaseService) { + + if(!service.hardDeleteItemByColumnName || !service.hardDeleteItemsOlderThanDays) { + continue; + } + + try { + // Retain data for 30 days for accidental deletion, and then hard delete. + await service.hardDeleteBy({ + query: { + [service.hardDeleteItemByColumnName]: QueryHelper.lessThan( + OneUptimeDate.getSomeDaysAgo(service.hardDeleteItemsOlderThanDays) + ), + }, + props: { + isRoot: true, + }, + limit: LIMIT_MAX, + skip: 0, + }); + } catch (err) { + logger.error(err); + } + } + } + } +); + + + +