This commit is contained in:
Simon Larsen
2023-05-02 13:33:56 +01:00
parent 0721970cc2
commit 746de4ea9a
9 changed files with 197 additions and 27 deletions

View File

@@ -34,9 +34,10 @@ const VerifyEmail: FunctionComponent = () => {
emailverificationToken,
EmailVerificationToken,
FormType.Create,
apiUrl,
{},
{}
{
overrideRequestUrl: apiUrl,
}
);
} catch (err) {
setError(API.getFriendlyMessage(err));

View File

@@ -1,3 +1,5 @@
import BadDataException from './Exception/BadDataException';
import { JSONObject } from './JSON';
import PositiveNumber from './PositiveNumber';
import moment from 'moment-timezone';
@@ -373,8 +375,18 @@ export default class OneUptimeDate {
return moment(date).isAfter(new Date());
}
public static fromString(date: string): Date {
return moment(date).toDate();
public static fromString(date: string | JSONObject): Date {
if(typeof date === 'string') {
return moment(date).toDate();
}
if(date && date['value'] && typeof date['value'] === 'string'){
return moment(date['value']).toDate();
}
throw new BadDataException('Invalid date');
}
public static asDateForDatabaseQuery(date: string | Date): string {

View File

@@ -0,0 +1,56 @@
import Probe from 'Model/Models/Probe';
import UserMiddleware from '../Middleware/UserAuthorization';
import ProbeService, {
Service as ProbeServiceType,
} from '../Services/ProbeService';
import {
ExpressRequest,
ExpressResponse,
NextFunction,
} from '../Utils/Express';
import Response from '../Utils/Response';
import BaseAPI from './BaseAPI';
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
import PositiveNumber from 'Common/Types/PositiveNumber';
export default class ProbeAPI extends BaseAPI<
Probe,
ProbeServiceType
> {
public constructor() {
super(Probe, ProbeService);
this.router.post(
`${new this.entityType().getCrudApiPath()?.toString()}/global-probes`,
UserMiddleware.getUserMiddleware,
async (
req: ExpressRequest,
res: ExpressResponse,
next: NextFunction
) => {
try {
const probes = await ProbeService.findBy({
query: {
isGlobalProbe: true,
},
select: {
name: true,
description: true,
lastAlive: true,
iconFileId: true,
},
props: {
isRoot: true,
},
skip: 0,
limit: LIMIT_MAX
})
return Response.sendEntityArrayResponse(req, res, probes, new PositiveNumber(probes.length), Probe);
} catch (err) {
next(err);
}
}
);
}
}

View File

@@ -109,7 +109,9 @@ const FilePicker: FunctionComponent<ComponentProps> = (
(await ModelAPI.create<FileModel>(
fileModel,
FileModel,
CommonURL.fromURL(FILE_URL).addRoute('/file')
{
overrideRequestUrl: CommonURL.fromURL(FILE_URL).addRoute('/file')
}
)) as HTTPResponse<FileModel>;
filesResult.push(result.data as FileModel);
}

View File

@@ -453,11 +453,11 @@ const ModelForm: Function = <TBaseModel extends BaseModel>(
tBaseModel as TBaseModel,
props.modelType,
props.formType,
props.apiUrl,
miscDataProps,
{
...props.saveRequestOptions,
requestHeaders: props.requestHeaders,
overrideRequestUrl: props.apiUrl,
}
);

View File

@@ -31,13 +31,13 @@ export interface ListResult<TBaseModel extends BaseModel> extends JSONObject {
export interface RequestOptions {
isMultiTenantRequest?: boolean | undefined;
requestHeaders?: Dictionary<string> | undefined;
overrideRequestUrl?: URL | undefined;
}
export default class ModelAPI {
public static async create<TBaseModel extends BaseModel>(
model: TBaseModel,
modelType: { new (): TBaseModel },
apiUrlOverride?: URL,
requestOptions?: RequestOptions | undefined
): Promise<
HTTPResponse<JSONObject | JSONArray | TBaseModel | Array<TBaseModel>>
@@ -46,7 +46,6 @@ export default class ModelAPI {
model,
modelType,
FormType.Create,
apiUrlOverride,
{},
requestOptions
);
@@ -55,7 +54,6 @@ export default class ModelAPI {
public static async update<TBaseModel extends BaseModel>(
model: TBaseModel,
modelType: { new (): TBaseModel },
apiUrlOverride?: URL
): Promise<
HTTPResponse<JSONObject | JSONArray | TBaseModel | Array<TBaseModel>>
> {
@@ -63,7 +61,6 @@ export default class ModelAPI {
model,
modelType,
FormType.Update,
apiUrlOverride
);
}
@@ -118,13 +115,12 @@ export default class ModelAPI {
model: TBaseModel,
modelType: { new (): TBaseModel },
formType: FormType,
apiUrlOverride?: URL,
miscDataProps?: JSONObject,
requestOptions?: RequestOptions | undefined
): Promise<
HTTPResponse<JSONObject | JSONArray | TBaseModel | Array<TBaseModel>>
> {
let apiUrl: URL | null = apiUrlOverride || null;
let apiUrl: URL | null = requestOptions?.overrideRequestUrl || null;
if (!apiUrl) {
const apiPath: Route | null = model.getCrudApiPath();
@@ -190,10 +186,14 @@ export default class ModelAPI {
);
}
const apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
.addRoute(apiPath)
.addRoute('/get-list');
if(requestOptions?.overrideRequestUrl){
apiUrl = requestOptions.overrideRequestUrl;
}
if (!apiUrl) {
throw new BadDataException(
'This model does not support list operations.'
@@ -257,10 +257,14 @@ export default class ModelAPI {
);
}
const apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
.addRoute(apiPath)
.addRoute('/count');
if(requestOptions?.overrideRequestUrl){
apiUrl = requestOptions.overrideRequestUrl;
}
if (!apiUrl) {
throw new BadDataException(
'This model does not support count operations.'
@@ -335,11 +339,15 @@ export default class ModelAPI {
);
}
const apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
.addRoute(apiPath)
.addRoute('/' + id.toString())
.addRoute('/get-item');
if(requestOptions?.overrideRequestUrl){
apiUrl = requestOptions.overrideRequestUrl;
}
if (!apiUrl) {
throw new BadDataException(
'This model does not support get operations.'

View File

@@ -19,6 +19,8 @@ import ProbeElement from '../../Components/Probe/Probe';
import Statusbubble from 'CommonUI/src/Components/StatusBubble/StatusBubble';
import { Green, Red } from 'Common/Types/BrandColors';
import OneUptimeDate from 'Common/Types/Date';
import URL from 'Common/Types/API/URL';
import { DASHBOARD_API_URL } from 'CommonUI/src/Config';
const ProbePage: FunctionComponent<PageComponentProps> = (
_props: PageComponentProps
@@ -56,6 +58,75 @@ const ProbePage: FunctionComponent<PageComponentProps> = (
>
<>
<ModelTable<Probe>
modelType={Probe}
id="probes-table"
name="Settings > Global Probes"
isDeleteable={false}
isEditable={false}
isCreateable={false}
cardProps={{
icon: IconProp.Signal,
title: 'Global Probes',
description:
'Global Probes help you monitor external resources from different locations around the world.',
}}
fetchRequestOptions={{
overrideRequestUrl: URL.fromString(DASHBOARD_API_URL.toString()).addRoute('/probe/global-probes')
}}
noItemsMessage={'No probes found.'}
showRefreshButton={true}
showFilterButton={true}
columns={[
{
field: {
name: true,
},
title: 'Name',
type: FieldType.Text,
isFilterable: true,
getElement: (item: JSONObject): ReactElement => {
return (
<ProbeElement probe={item}/>
);
},
},
{
field: {
description: true,
},
title: 'Description',
type: FieldType.Text,
isFilterable: true,
},
{
field: {
lastAlive: true,
},
title: 'Status',
type: FieldType.Text,
isFilterable: false,
getElement: (item: JSONObject): ReactElement => {
if(item && item['lastAlive'] && OneUptimeDate.getNumberOfMinutesBetweenDates(OneUptimeDate.fromString(item['lastAlive'] as string), OneUptimeDate.getCurrentDate()) < 5){
return <Statusbubble text={'Connected'} color={Green}/>
}
return (
<Statusbubble text={'Disconnected'} color={Red}/>
);
},
},
]}
/>
<ModelTable<Probe>
modelType={Probe}
query={{
@@ -168,8 +239,8 @@ const ProbePage: FunctionComponent<PageComponentProps> = (
isFilterable: false,
getElement: (item: JSONObject): ReactElement => {
if(item && item['lastAlive'] && OneUptimeDate.getNumberOfMinutesBetweenDates(item['lastAlive'] as Date, OneUptimeDate.getCurrentDate()) < 5){
<Statusbubble text={'Connected'} color={Green}/>
if(item && item['lastAlive'] && OneUptimeDate.getNumberOfMinutesBetweenDates(OneUptimeDate.fromString(item['lastAlive'] as string), OneUptimeDate.getCurrentDate()) < 5){
return <Statusbubble text={'Connected'} color={Green}/>
}
return (

View File

@@ -45,10 +45,6 @@ import WorkflowVariableService, {
Service as WorkflowVariableServiceType,
} from 'CommonServer/Services/WorkflowVariableService';
import Probe from 'Model/Models/Probe';
import ProbeService, {
Service as ProbeServiceType,
} from 'CommonServer/Services/ProbeService';
import MonitorProbe from 'Model/Models/MonitorProbe';
import MonitorProbeService, {
@@ -214,6 +210,8 @@ import StatusPageDomainService, {
import StatusPageAPI from 'CommonServer/API/StatusPageAPI';
import ProbeAPI from 'CommonServer/API/ProbeAPI';
import StatusPageSubscriberAPI from 'CommonServer/API/StatusPageSubscriberAPI';
// Custom Fields API
@@ -255,12 +253,6 @@ app.use(
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<Probe, ProbeServiceType>(Probe, ProbeService).getRouter()
);
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new BaseAPI<MonitorProbe, MonitorProbeServiceType>(MonitorProbe, MonitorProbeService).getRouter()
@@ -518,6 +510,9 @@ app.use(
);
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new StatusPageAPI().getRouter());
app.use(`/${APP_NAME.toLocaleLowerCase()}`, new ProbeAPI().getRouter());
app.use(
`/${APP_NAME.toLocaleLowerCase()}`,
new StatusPageSubscriberAPI().getRouter()

View File

@@ -410,4 +410,29 @@ export default class Probe extends BaseModel {
transformer: ObjectID.getDatabaseTransformer(),
})
public createdByUserId?: ObjectID = undefined;
@ColumnAccessControl({
create: [
],
read: [
],
update: [
]
})
@TableColumn({
isDefaultValueColumn: true,
required: true,
type: TableColumnType.Boolean,
})
@Column({
type: ColumnType.Boolean,
nullable: false,
unique: false,
default: false,
})
public isGlobalProbe?: boolean = undefined;
}