camstreamerlib 4.0.16 → 4.0.17-dev-milestone.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/PlaneTrackerAPI.d.ts +9 -0
- package/cjs/node/events/MilestoneAgent.d.ts +14 -0
- package/cjs/node/events/MilestoneAgent.js +124 -0
- package/cjs/node/events/MilestoneAgent.test.d.ts +1 -0
- package/cjs/node/events/MilestoneAgent.test.js +102 -0
- package/cjs/node/index.d.ts +2 -0
- package/cjs/node/index.js +2 -0
- package/cjs/types/MilestoneAgent.d.ts +81 -0
- package/cjs/types/MilestoneAgent.js +18 -0
- package/cjs/types/PlaneTrackerAPI.d.ts +44 -0
- package/cjs/types/PlaneTrackerAPI.js +14 -0
- package/esm/node/events/MilestoneAgent.js +120 -0
- package/esm/node/events/MilestoneAgent.test.js +100 -0
- package/esm/node/index.js +2 -0
- package/esm/types/MilestoneAgent.js +15 -0
- package/esm/types/PlaneTrackerAPI.js +14 -0
- package/package.json +1 -1
- package/types/PlaneTrackerAPI.d.ts +9 -0
- package/types/node/events/MilestoneAgent.d.ts +14 -0
- package/types/node/events/MilestoneAgent.test.d.ts +1 -0
- package/types/node/index.d.ts +2 -0
- package/types/types/MilestoneAgent.d.ts +81 -0
- package/types/types/PlaneTrackerAPI.d.ts +44 -0
package/cjs/PlaneTrackerAPI.d.ts
CHANGED
|
@@ -107,6 +107,15 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
|
|
|
107
107
|
protocol: "https" | "http" | "https_insecure";
|
|
108
108
|
sourceKey: string;
|
|
109
109
|
};
|
|
110
|
+
milestone: {
|
|
111
|
+
ip: string;
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
port: number;
|
|
114
|
+
cameraList: string[];
|
|
115
|
+
pass: string;
|
|
116
|
+
user: string;
|
|
117
|
+
protocol: "https" | "http" | "https_insecure";
|
|
118
|
+
};
|
|
110
119
|
camstreamerIntegration: {
|
|
111
120
|
adPlacementEnabled: boolean;
|
|
112
121
|
adMinIntervalSec: number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MilestoneAgentOptions, TBookmark, TMilestoneCamera } from '../../types/MilestoneAgent';
|
|
2
|
+
export declare class MilestoneAgent {
|
|
3
|
+
private settings;
|
|
4
|
+
private protocol;
|
|
5
|
+
private sender;
|
|
6
|
+
private token?;
|
|
7
|
+
constructor(options?: MilestoneAgentOptions);
|
|
8
|
+
checkConnection(): Promise<void>;
|
|
9
|
+
getAllCameras(): Promise<TMilestoneCamera[]>;
|
|
10
|
+
sendBookmark(cameraId: string, bookmark: TBookmark): Promise<void>;
|
|
11
|
+
private getCamerasPage;
|
|
12
|
+
private getToken;
|
|
13
|
+
private getRequestOptions;
|
|
14
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MilestoneAgent = void 0;
|
|
4
|
+
const errors_1 = require("../../errors/errors");
|
|
5
|
+
const HttpRequestSender_1 = require("../HttpRequestSender");
|
|
6
|
+
const MilestoneAgent_1 = require("../../types/MilestoneAgent");
|
|
7
|
+
const CLIENT_ID = 'GrantValidatorClient';
|
|
8
|
+
const PAGE_SIZE = 100;
|
|
9
|
+
const TOKEN_EXPIRY_MARGIN_SEC = 60;
|
|
10
|
+
class MilestoneAgent {
|
|
11
|
+
settings;
|
|
12
|
+
protocol;
|
|
13
|
+
sender;
|
|
14
|
+
token;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.settings = {
|
|
17
|
+
protocol: options.protocol ?? 'https_insecure',
|
|
18
|
+
ip: options.ip ?? '127.0.0.1',
|
|
19
|
+
port: options.port ?? 443,
|
|
20
|
+
user: options.user ?? '',
|
|
21
|
+
pass: options.pass ?? '',
|
|
22
|
+
timeout: options.timeout ?? 10000,
|
|
23
|
+
};
|
|
24
|
+
this.protocol = this.settings.protocol === 'http' ? 'http:' : 'https:';
|
|
25
|
+
const tlsInsecure = this.settings.protocol === 'https_insecure';
|
|
26
|
+
this.sender = new HttpRequestSender_1.HttpRequestSender({ rejectUnaurhorized: !tlsInsecure });
|
|
27
|
+
}
|
|
28
|
+
async checkConnection() {
|
|
29
|
+
await this.getToken(true);
|
|
30
|
+
await this.getCamerasPage(1, 1);
|
|
31
|
+
}
|
|
32
|
+
async getAllCameras() {
|
|
33
|
+
const cameras = [];
|
|
34
|
+
let page = 1;
|
|
35
|
+
for (;;) {
|
|
36
|
+
const pageCameras = await this.getCamerasPage(page, PAGE_SIZE);
|
|
37
|
+
cameras.push(...pageCameras);
|
|
38
|
+
if (pageCameras.length < PAGE_SIZE) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
page += 1;
|
|
42
|
+
}
|
|
43
|
+
return cameras;
|
|
44
|
+
}
|
|
45
|
+
async sendBookmark(cameraId, bookmark) {
|
|
46
|
+
const token = await this.getToken();
|
|
47
|
+
const body = JSON.stringify({
|
|
48
|
+
...bookmark,
|
|
49
|
+
devicePath: { type: 'cameras', id: cameraId },
|
|
50
|
+
});
|
|
51
|
+
const res = await this.sender.sendRequest(this.getRequestOptions('POST', '/api/rest/v1/bookmarks', token, {
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
}), body);
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
throw new errors_1.ErrorWithResponse(res);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async getCamerasPage(page, itemsPerPage) {
|
|
59
|
+
const token = await this.getToken();
|
|
60
|
+
const res = await this.sender.sendRequest(this.getRequestOptions('GET', `/api/rest/v1/cameras?page=${page}&itemsPerPage=${itemsPerPage}`, token));
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
throw new errors_1.ErrorWithResponse(res);
|
|
63
|
+
}
|
|
64
|
+
const responseBody = await res.text();
|
|
65
|
+
const result = await MilestoneAgent_1.camerasResponseSchema.safeParseAsync(JSON.parse(responseBody));
|
|
66
|
+
if (!result.success) {
|
|
67
|
+
throw new Error('Milestone get cameras failed: ' + JSON.stringify(result.error.issues) + '\n' + responseBody);
|
|
68
|
+
}
|
|
69
|
+
return result.data.array;
|
|
70
|
+
}
|
|
71
|
+
async getToken(forceRefresh = false) {
|
|
72
|
+
const nowSec = Date.now() / 1000;
|
|
73
|
+
if (!forceRefresh && this.token !== undefined && this.token.expiresAt > nowSec) {
|
|
74
|
+
return this.token.value;
|
|
75
|
+
}
|
|
76
|
+
const body = new URLSearchParams({
|
|
77
|
+
grant_type: 'password',
|
|
78
|
+
username: this.settings.user,
|
|
79
|
+
password: this.settings.pass,
|
|
80
|
+
client_id: CLIENT_ID,
|
|
81
|
+
}).toString();
|
|
82
|
+
const res = await this.sender.sendRequest({
|
|
83
|
+
method: 'POST',
|
|
84
|
+
protocol: this.protocol,
|
|
85
|
+
host: this.settings.ip,
|
|
86
|
+
port: this.settings.port,
|
|
87
|
+
path: '/idp/connect/token',
|
|
88
|
+
timeout: this.settings.timeout,
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
91
|
+
'Accept': 'application/json',
|
|
92
|
+
},
|
|
93
|
+
}, body);
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
throw new errors_1.ErrorWithResponse(res);
|
|
96
|
+
}
|
|
97
|
+
const responseBody = await res.text();
|
|
98
|
+
const result = await MilestoneAgent_1.tokenResponseSchema.safeParseAsync(JSON.parse(responseBody));
|
|
99
|
+
if (!result.success) {
|
|
100
|
+
throw new Error('Milestone authorization failed: ' + JSON.stringify(result.error.issues) + '\n' + responseBody);
|
|
101
|
+
}
|
|
102
|
+
this.token = {
|
|
103
|
+
value: result.data.access_token,
|
|
104
|
+
expiresAt: nowSec + result.data.expires_in - TOKEN_EXPIRY_MARGIN_SEC,
|
|
105
|
+
};
|
|
106
|
+
return this.token.value;
|
|
107
|
+
}
|
|
108
|
+
getRequestOptions(method, path, token, extraHeaders) {
|
|
109
|
+
return {
|
|
110
|
+
method,
|
|
111
|
+
protocol: this.protocol,
|
|
112
|
+
host: this.settings.ip,
|
|
113
|
+
port: this.settings.port,
|
|
114
|
+
path,
|
|
115
|
+
timeout: this.settings.timeout,
|
|
116
|
+
headers: {
|
|
117
|
+
Authorization: `Bearer ${token}`,
|
|
118
|
+
Accept: 'application/json',
|
|
119
|
+
...extraHeaders,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.MilestoneAgent = MilestoneAgent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const HttpRequestSender_1 = require("../HttpRequestSender");
|
|
5
|
+
const MilestoneAgent_1 = require("./MilestoneAgent");
|
|
6
|
+
globals_1.jest.mock('../HttpRequestSender');
|
|
7
|
+
function jsonResponse(body, status = 200) {
|
|
8
|
+
return {
|
|
9
|
+
ok: status >= 200 && status < 300,
|
|
10
|
+
status,
|
|
11
|
+
statusText: 'OK',
|
|
12
|
+
text: () => Promise.resolve(JSON.stringify(body)),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const TOKEN_BODY = { access_token: 'abc123', expires_in: 3600, token_type: 'Bearer', scope: 'managementserver' };
|
|
16
|
+
(0, globals_1.describe)('MilestoneAgent', () => {
|
|
17
|
+
const MockedSender = globals_1.jest.mocked(HttpRequestSender_1.HttpRequestSender);
|
|
18
|
+
let sendRequest;
|
|
19
|
+
(0, globals_1.beforeEach)(() => {
|
|
20
|
+
sendRequest = globals_1.jest.fn();
|
|
21
|
+
MockedSender.mockImplementation(() => ({ sendRequest }));
|
|
22
|
+
});
|
|
23
|
+
function callsTo(prefix) {
|
|
24
|
+
return sendRequest.mock.calls.filter((c) => c[0].path.startsWith(prefix));
|
|
25
|
+
}
|
|
26
|
+
function routeByPath(handlers) {
|
|
27
|
+
sendRequest.mockImplementation((...args) => {
|
|
28
|
+
const options = args[0];
|
|
29
|
+
if (options.path === '/idp/connect/token') {
|
|
30
|
+
return Promise.resolve(jsonResponse(TOKEN_BODY));
|
|
31
|
+
}
|
|
32
|
+
for (const [prefix, handler] of Object.entries(handlers)) {
|
|
33
|
+
if (options.path.startsWith(prefix)) {
|
|
34
|
+
if (typeof handler === 'function') {
|
|
35
|
+
const page = Number(new URLSearchParams(options.path.split('?')[1]).get('page'));
|
|
36
|
+
return Promise.resolve(handler(page));
|
|
37
|
+
}
|
|
38
|
+
return Promise.resolve(handler);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Unexpected path ${options.path}`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
(0, globals_1.test)('fetches a bearer token before listing cameras and sends it in the header', async () => {
|
|
45
|
+
routeByPath({ '/api/rest/v1/cameras': jsonResponse({ array: [{ id: 'cam-1', displayName: 'Cam 1' }] }) });
|
|
46
|
+
const agent = new MilestoneAgent_1.MilestoneAgent({ ip: '1.2.3.4' });
|
|
47
|
+
const cameras = await agent.getAllCameras();
|
|
48
|
+
(0, globals_1.expect)(cameras).toEqual([{ id: 'cam-1', displayName: 'Cam 1' }]);
|
|
49
|
+
const tokenCalls = callsTo('/idp/connect/token');
|
|
50
|
+
(0, globals_1.expect)(tokenCalls.length).toBe(1);
|
|
51
|
+
(0, globals_1.expect)(tokenCalls[0][0].method).toBe('POST');
|
|
52
|
+
const camerasCall = callsTo('/api/rest/v1/cameras')[0];
|
|
53
|
+
(0, globals_1.expect)(camerasCall[0].headers.Authorization).toBe('Bearer abc123');
|
|
54
|
+
});
|
|
55
|
+
(0, globals_1.test)('reuses the cached token across calls', async () => {
|
|
56
|
+
routeByPath({ '/api/rest/v1/cameras': jsonResponse({ array: [] }) });
|
|
57
|
+
const agent = new MilestoneAgent_1.MilestoneAgent({ ip: '1.2.3.4' });
|
|
58
|
+
await agent.getAllCameras();
|
|
59
|
+
await agent.getAllCameras();
|
|
60
|
+
(0, globals_1.expect)(callsTo('/idp/connect/token').length).toBe(1);
|
|
61
|
+
});
|
|
62
|
+
(0, globals_1.test)('pages through cameras until a non-full page is returned', async () => {
|
|
63
|
+
const fullPage = (page) => jsonResponse({ array: Array.from({ length: 100 }, (_, i) => ({ id: `p${page}-${i}` })) });
|
|
64
|
+
routeByPath({
|
|
65
|
+
'/api/rest/v1/cameras': (page) => page < 3 ? fullPage(page) : jsonResponse({ array: [{ id: 'last' }] }),
|
|
66
|
+
});
|
|
67
|
+
const agent = new MilestoneAgent_1.MilestoneAgent({ ip: '1.2.3.4' });
|
|
68
|
+
const cameras = await agent.getAllCameras();
|
|
69
|
+
(0, globals_1.expect)(cameras.length).toBe(201);
|
|
70
|
+
(0, globals_1.expect)(cameras[cameras.length - 1]).toEqual({ id: 'last' });
|
|
71
|
+
});
|
|
72
|
+
(0, globals_1.test)('sends a bookmark with the camera devicePath', async () => {
|
|
73
|
+
routeByPath({ '/api/rest/v1/bookmarks': jsonResponse({ result: {} }, 201) });
|
|
74
|
+
const agent = new MilestoneAgent_1.MilestoneAgent({ ip: '1.2.3.4' });
|
|
75
|
+
await agent.sendBookmark('cam-guid', {
|
|
76
|
+
header: 'Airbus A320',
|
|
77
|
+
description: 'ICAO BC4AA',
|
|
78
|
+
timeBegin: '2026-04-16T10:25:00.000Z',
|
|
79
|
+
timeEnd: '2026-04-16T10:25:00.000Z',
|
|
80
|
+
timeTriggered: '2026-04-16T10:25:00.000Z',
|
|
81
|
+
reference: 'BC4AA',
|
|
82
|
+
});
|
|
83
|
+
const call = callsTo('/api/rest/v1/bookmarks')[0];
|
|
84
|
+
(0, globals_1.expect)(call).toBeDefined();
|
|
85
|
+
const body = JSON.parse(call[1]);
|
|
86
|
+
(0, globals_1.expect)(body.devicePath).toEqual({ type: 'cameras', id: 'cam-guid' });
|
|
87
|
+
(0, globals_1.expect)(body.header).toBe('Airbus A320');
|
|
88
|
+
(0, globals_1.expect)(body.description).toBe('ICAO BC4AA');
|
|
89
|
+
});
|
|
90
|
+
(0, globals_1.test)('throws on a non-ok bookmark response', async () => {
|
|
91
|
+
routeByPath({ '/api/rest/v1/bookmarks': jsonResponse({ error: 'nope' }, 500) });
|
|
92
|
+
const agent = new MilestoneAgent_1.MilestoneAgent({ ip: '1.2.3.4' });
|
|
93
|
+
await (0, globals_1.expect)(agent.sendBookmark('cam-guid', {
|
|
94
|
+
header: 'h',
|
|
95
|
+
description: 'd',
|
|
96
|
+
timeBegin: 't',
|
|
97
|
+
timeEnd: 't',
|
|
98
|
+
timeTriggered: 't',
|
|
99
|
+
reference: 'r',
|
|
100
|
+
})).rejects.toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
package/cjs/node/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './events/AxisCameraStationEvents';
|
|
|
6
6
|
export * from './TimeZoneDaemon';
|
|
7
7
|
export * from './events/GenetecAgent';
|
|
8
8
|
export * from '../types/GenetecAgent';
|
|
9
|
+
export * from './events/MilestoneAgent';
|
|
10
|
+
export * from '../types/MilestoneAgent';
|
|
9
11
|
export { ResourceManager } from './CamOverlayPainter/ResourceManager';
|
|
10
12
|
export { Painter } from './CamOverlayPainter/Painter';
|
|
11
13
|
export { Frame } from './CamOverlayPainter/Frame';
|
package/cjs/node/index.js
CHANGED
|
@@ -23,6 +23,8 @@ __exportStar(require("./events/AxisCameraStationEvents"), exports);
|
|
|
23
23
|
__exportStar(require("./TimeZoneDaemon"), exports);
|
|
24
24
|
__exportStar(require("./events/GenetecAgent"), exports);
|
|
25
25
|
__exportStar(require("../types/GenetecAgent"), exports);
|
|
26
|
+
__exportStar(require("./events/MilestoneAgent"), exports);
|
|
27
|
+
__exportStar(require("../types/MilestoneAgent"), exports);
|
|
26
28
|
var ResourceManager_1 = require("./CamOverlayPainter/ResourceManager");
|
|
27
29
|
Object.defineProperty(exports, "ResourceManager", { enumerable: true, get: function () { return ResourceManager_1.ResourceManager; } });
|
|
28
30
|
var Painter_1 = require("./CamOverlayPainter/Painter");
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export type MilestoneAgentOptions = {
|
|
3
|
+
protocol?: 'http' | 'https' | 'https_insecure';
|
|
4
|
+
ip?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
user?: string;
|
|
7
|
+
pass?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const tokenResponseSchema: z.ZodObject<{
|
|
11
|
+
access_token: z.ZodString;
|
|
12
|
+
expires_in: z.ZodNumber;
|
|
13
|
+
token_type: z.ZodString;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
access_token: string;
|
|
16
|
+
expires_in: number;
|
|
17
|
+
token_type: string;
|
|
18
|
+
}, {
|
|
19
|
+
access_token: string;
|
|
20
|
+
expires_in: number;
|
|
21
|
+
token_type: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type TTokenResponse = z.infer<typeof tokenResponseSchema>;
|
|
24
|
+
export declare const milestoneCameraSchema: z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
name: z.ZodOptional<z.ZodString>;
|
|
27
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
28
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
id: string;
|
|
31
|
+
name?: string | undefined;
|
|
32
|
+
enabled?: boolean | undefined;
|
|
33
|
+
displayName?: string | undefined;
|
|
34
|
+
}, {
|
|
35
|
+
id: string;
|
|
36
|
+
name?: string | undefined;
|
|
37
|
+
enabled?: boolean | undefined;
|
|
38
|
+
displayName?: string | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
export type TMilestoneCamera = z.infer<typeof milestoneCameraSchema>;
|
|
41
|
+
export declare const camerasResponseSchema: z.ZodObject<{
|
|
42
|
+
array: z.ZodArray<z.ZodObject<{
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
name: z.ZodOptional<z.ZodString>;
|
|
45
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
46
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
id: string;
|
|
49
|
+
name?: string | undefined;
|
|
50
|
+
enabled?: boolean | undefined;
|
|
51
|
+
displayName?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
id: string;
|
|
54
|
+
name?: string | undefined;
|
|
55
|
+
enabled?: boolean | undefined;
|
|
56
|
+
displayName?: string | undefined;
|
|
57
|
+
}>, "many">;
|
|
58
|
+
}, "strip", z.ZodTypeAny, {
|
|
59
|
+
array: {
|
|
60
|
+
id: string;
|
|
61
|
+
name?: string | undefined;
|
|
62
|
+
enabled?: boolean | undefined;
|
|
63
|
+
displayName?: string | undefined;
|
|
64
|
+
}[];
|
|
65
|
+
}, {
|
|
66
|
+
array: {
|
|
67
|
+
id: string;
|
|
68
|
+
name?: string | undefined;
|
|
69
|
+
enabled?: boolean | undefined;
|
|
70
|
+
displayName?: string | undefined;
|
|
71
|
+
}[];
|
|
72
|
+
}>;
|
|
73
|
+
export type TCamerasResponse = z.infer<typeof camerasResponseSchema>;
|
|
74
|
+
export type TBookmark = {
|
|
75
|
+
header: string;
|
|
76
|
+
description: string;
|
|
77
|
+
timeBegin: string;
|
|
78
|
+
timeEnd: string;
|
|
79
|
+
timeTriggered: string;
|
|
80
|
+
reference: string;
|
|
81
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.camerasResponseSchema = exports.milestoneCameraSchema = exports.tokenResponseSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.tokenResponseSchema = zod_1.z.object({
|
|
6
|
+
access_token: zod_1.z.string(),
|
|
7
|
+
expires_in: zod_1.z.number(),
|
|
8
|
+
token_type: zod_1.z.string(),
|
|
9
|
+
});
|
|
10
|
+
exports.milestoneCameraSchema = zod_1.z.object({
|
|
11
|
+
id: zod_1.z.string(),
|
|
12
|
+
name: zod_1.z.string().optional(),
|
|
13
|
+
displayName: zod_1.z.string().optional(),
|
|
14
|
+
enabled: zod_1.z.boolean().optional(),
|
|
15
|
+
});
|
|
16
|
+
exports.camerasResponseSchema = zod_1.z.object({
|
|
17
|
+
array: zod_1.z.array(exports.milestoneCameraSchema),
|
|
18
|
+
});
|
|
@@ -358,6 +358,32 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
358
358
|
protocol: "https" | "http" | "https_insecure";
|
|
359
359
|
sourceKey: string;
|
|
360
360
|
}>>;
|
|
361
|
+
milestone: z.ZodDefault<z.ZodObject<{
|
|
362
|
+
protocol: z.ZodUnion<[z.ZodLiteral<"http">, z.ZodLiteral<"https">, z.ZodLiteral<"https_insecure">]>;
|
|
363
|
+
ip: z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>;
|
|
364
|
+
port: z.ZodNumber;
|
|
365
|
+
user: z.ZodString;
|
|
366
|
+
pass: z.ZodString;
|
|
367
|
+
} & {
|
|
368
|
+
enabled: z.ZodBoolean;
|
|
369
|
+
cameraList: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
370
|
+
}, "strip", z.ZodTypeAny, {
|
|
371
|
+
ip: string;
|
|
372
|
+
enabled: boolean;
|
|
373
|
+
port: number;
|
|
374
|
+
cameraList: string[];
|
|
375
|
+
pass: string;
|
|
376
|
+
user: string;
|
|
377
|
+
protocol: "https" | "http" | "https_insecure";
|
|
378
|
+
}, {
|
|
379
|
+
ip: string;
|
|
380
|
+
enabled: boolean;
|
|
381
|
+
port: number;
|
|
382
|
+
pass: string;
|
|
383
|
+
user: string;
|
|
384
|
+
protocol: "https" | "http" | "https_insecure";
|
|
385
|
+
cameraList?: string[] | undefined;
|
|
386
|
+
}>>;
|
|
361
387
|
camstreamerIntegration: z.ZodDefault<z.ZodObject<{
|
|
362
388
|
adPlacementEnabled: z.ZodBoolean;
|
|
363
389
|
adMinIntervalSec: z.ZodNumber;
|
|
@@ -463,6 +489,15 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
463
489
|
protocol: "https" | "http" | "https_insecure";
|
|
464
490
|
sourceKey: string;
|
|
465
491
|
};
|
|
492
|
+
milestone: {
|
|
493
|
+
ip: string;
|
|
494
|
+
enabled: boolean;
|
|
495
|
+
port: number;
|
|
496
|
+
cameraList: string[];
|
|
497
|
+
pass: string;
|
|
498
|
+
user: string;
|
|
499
|
+
protocol: "https" | "http" | "https_insecure";
|
|
500
|
+
};
|
|
466
501
|
camstreamerIntegration: {
|
|
467
502
|
adPlacementEnabled: boolean;
|
|
468
503
|
adMinIntervalSec: number;
|
|
@@ -606,6 +641,15 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
606
641
|
protocol: "https" | "http" | "https_insecure";
|
|
607
642
|
sourceKey: string;
|
|
608
643
|
} | undefined;
|
|
644
|
+
milestone?: {
|
|
645
|
+
ip: string;
|
|
646
|
+
enabled: boolean;
|
|
647
|
+
port: number;
|
|
648
|
+
pass: string;
|
|
649
|
+
user: string;
|
|
650
|
+
protocol: "https" | "http" | "https_insecure";
|
|
651
|
+
cameraList?: string[] | undefined;
|
|
652
|
+
} | undefined;
|
|
609
653
|
camstreamerIntegration?: {
|
|
610
654
|
adPlacementEnabled: boolean;
|
|
611
655
|
adMinIntervalSec: number;
|
|
@@ -202,6 +202,20 @@ exports.cameraSettingsSchema = zod_1.z.object({
|
|
|
202
202
|
pass: '',
|
|
203
203
|
sourceKey: '',
|
|
204
204
|
}),
|
|
205
|
+
milestone: exports.connectionSchema
|
|
206
|
+
.extend({
|
|
207
|
+
enabled: zod_1.z.boolean(),
|
|
208
|
+
cameraList: zod_1.z.string().array().default([]),
|
|
209
|
+
})
|
|
210
|
+
.default({
|
|
211
|
+
enabled: false,
|
|
212
|
+
protocol: 'https_insecure',
|
|
213
|
+
ip: '',
|
|
214
|
+
port: 443,
|
|
215
|
+
user: '',
|
|
216
|
+
pass: '',
|
|
217
|
+
cameraList: [],
|
|
218
|
+
}),
|
|
205
219
|
camstreamerIntegration: zod_1.z
|
|
206
220
|
.object({
|
|
207
221
|
adPlacementEnabled: zod_1.z.boolean(),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ErrorWithResponse } from '../../errors/errors';
|
|
2
|
+
import { HttpRequestSender } from '../HttpRequestSender';
|
|
3
|
+
import { camerasResponseSchema, tokenResponseSchema, } from '../../types/MilestoneAgent';
|
|
4
|
+
const CLIENT_ID = 'GrantValidatorClient';
|
|
5
|
+
const PAGE_SIZE = 100;
|
|
6
|
+
const TOKEN_EXPIRY_MARGIN_SEC = 60;
|
|
7
|
+
export class MilestoneAgent {
|
|
8
|
+
settings;
|
|
9
|
+
protocol;
|
|
10
|
+
sender;
|
|
11
|
+
token;
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.settings = {
|
|
14
|
+
protocol: options.protocol ?? 'https_insecure',
|
|
15
|
+
ip: options.ip ?? '127.0.0.1',
|
|
16
|
+
port: options.port ?? 443,
|
|
17
|
+
user: options.user ?? '',
|
|
18
|
+
pass: options.pass ?? '',
|
|
19
|
+
timeout: options.timeout ?? 10000,
|
|
20
|
+
};
|
|
21
|
+
this.protocol = this.settings.protocol === 'http' ? 'http:' : 'https:';
|
|
22
|
+
const tlsInsecure = this.settings.protocol === 'https_insecure';
|
|
23
|
+
this.sender = new HttpRequestSender({ rejectUnaurhorized: !tlsInsecure });
|
|
24
|
+
}
|
|
25
|
+
async checkConnection() {
|
|
26
|
+
await this.getToken(true);
|
|
27
|
+
await this.getCamerasPage(1, 1);
|
|
28
|
+
}
|
|
29
|
+
async getAllCameras() {
|
|
30
|
+
const cameras = [];
|
|
31
|
+
let page = 1;
|
|
32
|
+
for (;;) {
|
|
33
|
+
const pageCameras = await this.getCamerasPage(page, PAGE_SIZE);
|
|
34
|
+
cameras.push(...pageCameras);
|
|
35
|
+
if (pageCameras.length < PAGE_SIZE) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
page += 1;
|
|
39
|
+
}
|
|
40
|
+
return cameras;
|
|
41
|
+
}
|
|
42
|
+
async sendBookmark(cameraId, bookmark) {
|
|
43
|
+
const token = await this.getToken();
|
|
44
|
+
const body = JSON.stringify({
|
|
45
|
+
...bookmark,
|
|
46
|
+
devicePath: { type: 'cameras', id: cameraId },
|
|
47
|
+
});
|
|
48
|
+
const res = await this.sender.sendRequest(this.getRequestOptions('POST', '/api/rest/v1/bookmarks', token, {
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
}), body);
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new ErrorWithResponse(res);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async getCamerasPage(page, itemsPerPage) {
|
|
56
|
+
const token = await this.getToken();
|
|
57
|
+
const res = await this.sender.sendRequest(this.getRequestOptions('GET', `/api/rest/v1/cameras?page=${page}&itemsPerPage=${itemsPerPage}`, token));
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
throw new ErrorWithResponse(res);
|
|
60
|
+
}
|
|
61
|
+
const responseBody = await res.text();
|
|
62
|
+
const result = await camerasResponseSchema.safeParseAsync(JSON.parse(responseBody));
|
|
63
|
+
if (!result.success) {
|
|
64
|
+
throw new Error('Milestone get cameras failed: ' + JSON.stringify(result.error.issues) + '\n' + responseBody);
|
|
65
|
+
}
|
|
66
|
+
return result.data.array;
|
|
67
|
+
}
|
|
68
|
+
async getToken(forceRefresh = false) {
|
|
69
|
+
const nowSec = Date.now() / 1000;
|
|
70
|
+
if (!forceRefresh && this.token !== undefined && this.token.expiresAt > nowSec) {
|
|
71
|
+
return this.token.value;
|
|
72
|
+
}
|
|
73
|
+
const body = new URLSearchParams({
|
|
74
|
+
grant_type: 'password',
|
|
75
|
+
username: this.settings.user,
|
|
76
|
+
password: this.settings.pass,
|
|
77
|
+
client_id: CLIENT_ID,
|
|
78
|
+
}).toString();
|
|
79
|
+
const res = await this.sender.sendRequest({
|
|
80
|
+
method: 'POST',
|
|
81
|
+
protocol: this.protocol,
|
|
82
|
+
host: this.settings.ip,
|
|
83
|
+
port: this.settings.port,
|
|
84
|
+
path: '/idp/connect/token',
|
|
85
|
+
timeout: this.settings.timeout,
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
88
|
+
'Accept': 'application/json',
|
|
89
|
+
},
|
|
90
|
+
}, body);
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw new ErrorWithResponse(res);
|
|
93
|
+
}
|
|
94
|
+
const responseBody = await res.text();
|
|
95
|
+
const result = await tokenResponseSchema.safeParseAsync(JSON.parse(responseBody));
|
|
96
|
+
if (!result.success) {
|
|
97
|
+
throw new Error('Milestone authorization failed: ' + JSON.stringify(result.error.issues) + '\n' + responseBody);
|
|
98
|
+
}
|
|
99
|
+
this.token = {
|
|
100
|
+
value: result.data.access_token,
|
|
101
|
+
expiresAt: nowSec + result.data.expires_in - TOKEN_EXPIRY_MARGIN_SEC,
|
|
102
|
+
};
|
|
103
|
+
return this.token.value;
|
|
104
|
+
}
|
|
105
|
+
getRequestOptions(method, path, token, extraHeaders) {
|
|
106
|
+
return {
|
|
107
|
+
method,
|
|
108
|
+
protocol: this.protocol,
|
|
109
|
+
host: this.settings.ip,
|
|
110
|
+
port: this.settings.port,
|
|
111
|
+
path,
|
|
112
|
+
timeout: this.settings.timeout,
|
|
113
|
+
headers: {
|
|
114
|
+
Authorization: `Bearer ${token}`,
|
|
115
|
+
Accept: 'application/json',
|
|
116
|
+
...extraHeaders,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, jest } from '@jest/globals';
|
|
2
|
+
import { HttpRequestSender } from '../HttpRequestSender';
|
|
3
|
+
import { MilestoneAgent } from './MilestoneAgent';
|
|
4
|
+
jest.mock('../HttpRequestSender');
|
|
5
|
+
function jsonResponse(body, status = 200) {
|
|
6
|
+
return {
|
|
7
|
+
ok: status >= 200 && status < 300,
|
|
8
|
+
status,
|
|
9
|
+
statusText: 'OK',
|
|
10
|
+
text: () => Promise.resolve(JSON.stringify(body)),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const TOKEN_BODY = { access_token: 'abc123', expires_in: 3600, token_type: 'Bearer', scope: 'managementserver' };
|
|
14
|
+
describe('MilestoneAgent', () => {
|
|
15
|
+
const MockedSender = jest.mocked(HttpRequestSender);
|
|
16
|
+
let sendRequest;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
sendRequest = jest.fn();
|
|
19
|
+
MockedSender.mockImplementation(() => ({ sendRequest }));
|
|
20
|
+
});
|
|
21
|
+
function callsTo(prefix) {
|
|
22
|
+
return sendRequest.mock.calls.filter((c) => c[0].path.startsWith(prefix));
|
|
23
|
+
}
|
|
24
|
+
function routeByPath(handlers) {
|
|
25
|
+
sendRequest.mockImplementation((...args) => {
|
|
26
|
+
const options = args[0];
|
|
27
|
+
if (options.path === '/idp/connect/token') {
|
|
28
|
+
return Promise.resolve(jsonResponse(TOKEN_BODY));
|
|
29
|
+
}
|
|
30
|
+
for (const [prefix, handler] of Object.entries(handlers)) {
|
|
31
|
+
if (options.path.startsWith(prefix)) {
|
|
32
|
+
if (typeof handler === 'function') {
|
|
33
|
+
const page = Number(new URLSearchParams(options.path.split('?')[1]).get('page'));
|
|
34
|
+
return Promise.resolve(handler(page));
|
|
35
|
+
}
|
|
36
|
+
return Promise.resolve(handler);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`Unexpected path ${options.path}`);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
test('fetches a bearer token before listing cameras and sends it in the header', async () => {
|
|
43
|
+
routeByPath({ '/api/rest/v1/cameras': jsonResponse({ array: [{ id: 'cam-1', displayName: 'Cam 1' }] }) });
|
|
44
|
+
const agent = new MilestoneAgent({ ip: '1.2.3.4' });
|
|
45
|
+
const cameras = await agent.getAllCameras();
|
|
46
|
+
expect(cameras).toEqual([{ id: 'cam-1', displayName: 'Cam 1' }]);
|
|
47
|
+
const tokenCalls = callsTo('/idp/connect/token');
|
|
48
|
+
expect(tokenCalls.length).toBe(1);
|
|
49
|
+
expect(tokenCalls[0][0].method).toBe('POST');
|
|
50
|
+
const camerasCall = callsTo('/api/rest/v1/cameras')[0];
|
|
51
|
+
expect(camerasCall[0].headers.Authorization).toBe('Bearer abc123');
|
|
52
|
+
});
|
|
53
|
+
test('reuses the cached token across calls', async () => {
|
|
54
|
+
routeByPath({ '/api/rest/v1/cameras': jsonResponse({ array: [] }) });
|
|
55
|
+
const agent = new MilestoneAgent({ ip: '1.2.3.4' });
|
|
56
|
+
await agent.getAllCameras();
|
|
57
|
+
await agent.getAllCameras();
|
|
58
|
+
expect(callsTo('/idp/connect/token').length).toBe(1);
|
|
59
|
+
});
|
|
60
|
+
test('pages through cameras until a non-full page is returned', async () => {
|
|
61
|
+
const fullPage = (page) => jsonResponse({ array: Array.from({ length: 100 }, (_, i) => ({ id: `p${page}-${i}` })) });
|
|
62
|
+
routeByPath({
|
|
63
|
+
'/api/rest/v1/cameras': (page) => page < 3 ? fullPage(page) : jsonResponse({ array: [{ id: 'last' }] }),
|
|
64
|
+
});
|
|
65
|
+
const agent = new MilestoneAgent({ ip: '1.2.3.4' });
|
|
66
|
+
const cameras = await agent.getAllCameras();
|
|
67
|
+
expect(cameras.length).toBe(201);
|
|
68
|
+
expect(cameras[cameras.length - 1]).toEqual({ id: 'last' });
|
|
69
|
+
});
|
|
70
|
+
test('sends a bookmark with the camera devicePath', async () => {
|
|
71
|
+
routeByPath({ '/api/rest/v1/bookmarks': jsonResponse({ result: {} }, 201) });
|
|
72
|
+
const agent = new MilestoneAgent({ ip: '1.2.3.4' });
|
|
73
|
+
await agent.sendBookmark('cam-guid', {
|
|
74
|
+
header: 'Airbus A320',
|
|
75
|
+
description: 'ICAO BC4AA',
|
|
76
|
+
timeBegin: '2026-04-16T10:25:00.000Z',
|
|
77
|
+
timeEnd: '2026-04-16T10:25:00.000Z',
|
|
78
|
+
timeTriggered: '2026-04-16T10:25:00.000Z',
|
|
79
|
+
reference: 'BC4AA',
|
|
80
|
+
});
|
|
81
|
+
const call = callsTo('/api/rest/v1/bookmarks')[0];
|
|
82
|
+
expect(call).toBeDefined();
|
|
83
|
+
const body = JSON.parse(call[1]);
|
|
84
|
+
expect(body.devicePath).toEqual({ type: 'cameras', id: 'cam-guid' });
|
|
85
|
+
expect(body.header).toBe('Airbus A320');
|
|
86
|
+
expect(body.description).toBe('ICAO BC4AA');
|
|
87
|
+
});
|
|
88
|
+
test('throws on a non-ok bookmark response', async () => {
|
|
89
|
+
routeByPath({ '/api/rest/v1/bookmarks': jsonResponse({ error: 'nope' }, 500) });
|
|
90
|
+
const agent = new MilestoneAgent({ ip: '1.2.3.4' });
|
|
91
|
+
await expect(agent.sendBookmark('cam-guid', {
|
|
92
|
+
header: 'h',
|
|
93
|
+
description: 'd',
|
|
94
|
+
timeBegin: 't',
|
|
95
|
+
timeEnd: 't',
|
|
96
|
+
timeTriggered: 't',
|
|
97
|
+
reference: 'r',
|
|
98
|
+
})).rejects.toThrow();
|
|
99
|
+
});
|
|
100
|
+
});
|
package/esm/node/index.js
CHANGED
|
@@ -6,6 +6,8 @@ export * from './events/AxisCameraStationEvents';
|
|
|
6
6
|
export * from './TimeZoneDaemon';
|
|
7
7
|
export * from './events/GenetecAgent';
|
|
8
8
|
export * from '../types/GenetecAgent';
|
|
9
|
+
export * from './events/MilestoneAgent';
|
|
10
|
+
export * from '../types/MilestoneAgent';
|
|
9
11
|
export { ResourceManager } from './CamOverlayPainter/ResourceManager';
|
|
10
12
|
export { Painter } from './CamOverlayPainter/Painter';
|
|
11
13
|
export { Frame } from './CamOverlayPainter/Frame';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const tokenResponseSchema = z.object({
|
|
3
|
+
access_token: z.string(),
|
|
4
|
+
expires_in: z.number(),
|
|
5
|
+
token_type: z.string(),
|
|
6
|
+
});
|
|
7
|
+
export const milestoneCameraSchema = z.object({
|
|
8
|
+
id: z.string(),
|
|
9
|
+
name: z.string().optional(),
|
|
10
|
+
displayName: z.string().optional(),
|
|
11
|
+
enabled: z.boolean().optional(),
|
|
12
|
+
});
|
|
13
|
+
export const camerasResponseSchema = z.object({
|
|
14
|
+
array: z.array(milestoneCameraSchema),
|
|
15
|
+
});
|
|
@@ -199,6 +199,20 @@ export const cameraSettingsSchema = z.object({
|
|
|
199
199
|
pass: '',
|
|
200
200
|
sourceKey: '',
|
|
201
201
|
}),
|
|
202
|
+
milestone: connectionSchema
|
|
203
|
+
.extend({
|
|
204
|
+
enabled: z.boolean(),
|
|
205
|
+
cameraList: z.string().array().default([]),
|
|
206
|
+
})
|
|
207
|
+
.default({
|
|
208
|
+
enabled: false,
|
|
209
|
+
protocol: 'https_insecure',
|
|
210
|
+
ip: '',
|
|
211
|
+
port: 443,
|
|
212
|
+
user: '',
|
|
213
|
+
pass: '',
|
|
214
|
+
cameraList: [],
|
|
215
|
+
}),
|
|
202
216
|
camstreamerIntegration: z
|
|
203
217
|
.object({
|
|
204
218
|
adPlacementEnabled: z.boolean(),
|
package/package.json
CHANGED
|
@@ -107,6 +107,15 @@ export declare class PlaneTrackerAPI<Client extends IClient<TResponse, any>> ext
|
|
|
107
107
|
protocol: "https" | "http" | "https_insecure";
|
|
108
108
|
sourceKey: string;
|
|
109
109
|
};
|
|
110
|
+
milestone: {
|
|
111
|
+
ip: string;
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
port: number;
|
|
114
|
+
cameraList: string[];
|
|
115
|
+
pass: string;
|
|
116
|
+
user: string;
|
|
117
|
+
protocol: "https" | "http" | "https_insecure";
|
|
118
|
+
};
|
|
110
119
|
camstreamerIntegration: {
|
|
111
120
|
adPlacementEnabled: boolean;
|
|
112
121
|
adMinIntervalSec: number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MilestoneAgentOptions, TBookmark, TMilestoneCamera } from '../../types/MilestoneAgent';
|
|
2
|
+
export declare class MilestoneAgent {
|
|
3
|
+
private settings;
|
|
4
|
+
private protocol;
|
|
5
|
+
private sender;
|
|
6
|
+
private token?;
|
|
7
|
+
constructor(options?: MilestoneAgentOptions);
|
|
8
|
+
checkConnection(): Promise<void>;
|
|
9
|
+
getAllCameras(): Promise<TMilestoneCamera[]>;
|
|
10
|
+
sendBookmark(cameraId: string, bookmark: TBookmark): Promise<void>;
|
|
11
|
+
private getCamerasPage;
|
|
12
|
+
private getToken;
|
|
13
|
+
private getRequestOptions;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/types/node/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './events/AxisCameraStationEvents';
|
|
|
6
6
|
export * from './TimeZoneDaemon';
|
|
7
7
|
export * from './events/GenetecAgent';
|
|
8
8
|
export * from '../types/GenetecAgent';
|
|
9
|
+
export * from './events/MilestoneAgent';
|
|
10
|
+
export * from '../types/MilestoneAgent';
|
|
9
11
|
export { ResourceManager } from './CamOverlayPainter/ResourceManager';
|
|
10
12
|
export { Painter } from './CamOverlayPainter/Painter';
|
|
11
13
|
export { Frame } from './CamOverlayPainter/Frame';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export type MilestoneAgentOptions = {
|
|
3
|
+
protocol?: 'http' | 'https' | 'https_insecure';
|
|
4
|
+
ip?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
user?: string;
|
|
7
|
+
pass?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const tokenResponseSchema: z.ZodObject<{
|
|
11
|
+
access_token: z.ZodString;
|
|
12
|
+
expires_in: z.ZodNumber;
|
|
13
|
+
token_type: z.ZodString;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
access_token: string;
|
|
16
|
+
expires_in: number;
|
|
17
|
+
token_type: string;
|
|
18
|
+
}, {
|
|
19
|
+
access_token: string;
|
|
20
|
+
expires_in: number;
|
|
21
|
+
token_type: string;
|
|
22
|
+
}>;
|
|
23
|
+
export type TTokenResponse = z.infer<typeof tokenResponseSchema>;
|
|
24
|
+
export declare const milestoneCameraSchema: z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
name: z.ZodOptional<z.ZodString>;
|
|
27
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
28
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
id: string;
|
|
31
|
+
name?: string | undefined;
|
|
32
|
+
enabled?: boolean | undefined;
|
|
33
|
+
displayName?: string | undefined;
|
|
34
|
+
}, {
|
|
35
|
+
id: string;
|
|
36
|
+
name?: string | undefined;
|
|
37
|
+
enabled?: boolean | undefined;
|
|
38
|
+
displayName?: string | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
export type TMilestoneCamera = z.infer<typeof milestoneCameraSchema>;
|
|
41
|
+
export declare const camerasResponseSchema: z.ZodObject<{
|
|
42
|
+
array: z.ZodArray<z.ZodObject<{
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
name: z.ZodOptional<z.ZodString>;
|
|
45
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
46
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
id: string;
|
|
49
|
+
name?: string | undefined;
|
|
50
|
+
enabled?: boolean | undefined;
|
|
51
|
+
displayName?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
id: string;
|
|
54
|
+
name?: string | undefined;
|
|
55
|
+
enabled?: boolean | undefined;
|
|
56
|
+
displayName?: string | undefined;
|
|
57
|
+
}>, "many">;
|
|
58
|
+
}, "strip", z.ZodTypeAny, {
|
|
59
|
+
array: {
|
|
60
|
+
id: string;
|
|
61
|
+
name?: string | undefined;
|
|
62
|
+
enabled?: boolean | undefined;
|
|
63
|
+
displayName?: string | undefined;
|
|
64
|
+
}[];
|
|
65
|
+
}, {
|
|
66
|
+
array: {
|
|
67
|
+
id: string;
|
|
68
|
+
name?: string | undefined;
|
|
69
|
+
enabled?: boolean | undefined;
|
|
70
|
+
displayName?: string | undefined;
|
|
71
|
+
}[];
|
|
72
|
+
}>;
|
|
73
|
+
export type TCamerasResponse = z.infer<typeof camerasResponseSchema>;
|
|
74
|
+
export type TBookmark = {
|
|
75
|
+
header: string;
|
|
76
|
+
description: string;
|
|
77
|
+
timeBegin: string;
|
|
78
|
+
timeEnd: string;
|
|
79
|
+
timeTriggered: string;
|
|
80
|
+
reference: string;
|
|
81
|
+
};
|
|
@@ -358,6 +358,32 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
358
358
|
protocol: "https" | "http" | "https_insecure";
|
|
359
359
|
sourceKey: string;
|
|
360
360
|
}>>;
|
|
361
|
+
milestone: z.ZodDefault<z.ZodObject<{
|
|
362
|
+
protocol: z.ZodUnion<[z.ZodLiteral<"http">, z.ZodLiteral<"https">, z.ZodLiteral<"https_insecure">]>;
|
|
363
|
+
ip: z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>;
|
|
364
|
+
port: z.ZodNumber;
|
|
365
|
+
user: z.ZodString;
|
|
366
|
+
pass: z.ZodString;
|
|
367
|
+
} & {
|
|
368
|
+
enabled: z.ZodBoolean;
|
|
369
|
+
cameraList: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
370
|
+
}, "strip", z.ZodTypeAny, {
|
|
371
|
+
ip: string;
|
|
372
|
+
enabled: boolean;
|
|
373
|
+
port: number;
|
|
374
|
+
cameraList: string[];
|
|
375
|
+
pass: string;
|
|
376
|
+
user: string;
|
|
377
|
+
protocol: "https" | "http" | "https_insecure";
|
|
378
|
+
}, {
|
|
379
|
+
ip: string;
|
|
380
|
+
enabled: boolean;
|
|
381
|
+
port: number;
|
|
382
|
+
pass: string;
|
|
383
|
+
user: string;
|
|
384
|
+
protocol: "https" | "http" | "https_insecure";
|
|
385
|
+
cameraList?: string[] | undefined;
|
|
386
|
+
}>>;
|
|
361
387
|
camstreamerIntegration: z.ZodDefault<z.ZodObject<{
|
|
362
388
|
adPlacementEnabled: z.ZodBoolean;
|
|
363
389
|
adMinIntervalSec: z.ZodNumber;
|
|
@@ -463,6 +489,15 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
463
489
|
protocol: "https" | "http" | "https_insecure";
|
|
464
490
|
sourceKey: string;
|
|
465
491
|
};
|
|
492
|
+
milestone: {
|
|
493
|
+
ip: string;
|
|
494
|
+
enabled: boolean;
|
|
495
|
+
port: number;
|
|
496
|
+
cameraList: string[];
|
|
497
|
+
pass: string;
|
|
498
|
+
user: string;
|
|
499
|
+
protocol: "https" | "http" | "https_insecure";
|
|
500
|
+
};
|
|
466
501
|
camstreamerIntegration: {
|
|
467
502
|
adPlacementEnabled: boolean;
|
|
468
503
|
adMinIntervalSec: number;
|
|
@@ -606,6 +641,15 @@ export declare const cameraSettingsSchema: z.ZodObject<{
|
|
|
606
641
|
protocol: "https" | "http" | "https_insecure";
|
|
607
642
|
sourceKey: string;
|
|
608
643
|
} | undefined;
|
|
644
|
+
milestone?: {
|
|
645
|
+
ip: string;
|
|
646
|
+
enabled: boolean;
|
|
647
|
+
port: number;
|
|
648
|
+
pass: string;
|
|
649
|
+
user: string;
|
|
650
|
+
protocol: "https" | "http" | "https_insecure";
|
|
651
|
+
cameraList?: string[] | undefined;
|
|
652
|
+
} | undefined;
|
|
609
653
|
camstreamerIntegration?: {
|
|
610
654
|
adPlacementEnabled: boolean;
|
|
611
655
|
adMinIntervalSec: number;
|