@tak-ps/node-tak 8.2.1 → 8.4.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/.github/workflows/doc.yml +6 -6
- package/.github/workflows/release.yml +3 -3
- package/.github/workflows/test.yml +14 -9
- package/CHANGELOG.md +9 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/api/contacts.js +23 -0
- package/dist/lib/api/contacts.js.map +1 -0
- package/dist/lib/api/credentials.js +62 -0
- package/dist/lib/api/credentials.js.map +1 -0
- package/dist/lib/api/export.js +36 -0
- package/dist/lib/api/export.js.map +1 -0
- package/dist/lib/api/files.js +112 -0
- package/dist/lib/api/files.js.map +1 -0
- package/dist/lib/api/groups.js +47 -0
- package/dist/lib/api/groups.js.map +1 -0
- package/dist/lib/api/mission-layer.js +245 -0
- package/dist/lib/api/mission-layer.js.map +1 -0
- package/dist/lib/api/mission-log.js +108 -0
- package/dist/lib/api/mission-log.js.map +1 -0
- package/dist/lib/api/mission.js +583 -0
- package/dist/lib/api/mission.js.map +1 -0
- package/dist/lib/api/oauth.js +54 -0
- package/dist/lib/api/oauth.js.map +1 -0
- package/dist/lib/api/package.js +42 -0
- package/dist/lib/api/package.js.map +1 -0
- package/dist/lib/api/query.js +60 -0
- package/dist/lib/api/query.js.map +1 -0
- package/dist/lib/api/subscriptions.js +73 -0
- package/dist/lib/api/subscriptions.js.map +1 -0
- package/dist/lib/api/types.js +42 -0
- package/dist/lib/api/types.js.map +1 -0
- package/dist/lib/api/video.js +123 -0
- package/dist/lib/api/video.js.map +1 -0
- package/dist/lib/api.js +123 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/auth.js +92 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/fetch.js +26 -0
- package/dist/lib/fetch.js.map +1 -0
- package/dist/lib/stream.js +9 -0
- package/dist/lib/stream.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/index.ts +8 -3
- package/lib/api/contacts.ts +27 -0
- package/lib/api/credentials.ts +74 -0
- package/lib/api/export.ts +44 -0
- package/lib/api/files.ts +151 -0
- package/lib/api/groups.ts +63 -0
- package/lib/api/mission-layer.ts +312 -0
- package/lib/api/mission-log.ts +140 -0
- package/lib/api/mission.ts +741 -0
- package/lib/api/oauth.ts +68 -0
- package/lib/api/package.ts +56 -0
- package/lib/api/query.ts +79 -0
- package/lib/api/subscriptions.ts +84 -0
- package/lib/api/types.ts +43 -0
- package/lib/api/video.ts +155 -0
- package/lib/api.ts +136 -0
- package/lib/auth.ts +117 -0
- package/lib/fetch.ts +38 -0
- package/lib/stream.ts +10 -0
- package/package.json +17 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../lib/stream.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc;IACtD,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,IAAI,GAAG,KAAK,EAAU,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,CAAC,MAAM,CAAC,6BAA6B,GAAG,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../index.ts","../test/default.test.ts","../test/findCoT.test.ts"],"version":"5.8.
|
|
1
|
+
{"root":["../index.ts","../test/default.test.ts","../test/findCoT.test.ts","../lib/api.ts","../lib/auth.ts","../lib/fetch.ts","../lib/stream.ts","../lib/api/contacts.ts","../lib/api/credentials.ts","../lib/api/export.ts","../lib/api/files.ts","../lib/api/groups.ts","../lib/api/mission-layer.ts","../lib/api/mission-log.ts","../lib/api/mission.ts","../lib/api/oauth.ts","../lib/api/package.ts","../lib/api/query.ts","../lib/api/subscriptions.ts","../lib/api/types.ts","../lib/api/video.ts"],"version":"5.8.3"}
|
package/index.ts
CHANGED
|
@@ -3,6 +3,9 @@ import tls from 'node:tls';
|
|
|
3
3
|
import CoT from '@tak-ps/node-cot';
|
|
4
4
|
import type { TLSSocket } from 'node:tls'
|
|
5
5
|
|
|
6
|
+
export * from './lib/api.js';
|
|
7
|
+
export * from './lib/auth.js';
|
|
8
|
+
|
|
6
9
|
/* eslint-disable no-control-regex */
|
|
7
10
|
export const REGEX_CONTROL = /[\u000B-\u001F\u007F-\u009F]/g;
|
|
8
11
|
|
|
@@ -127,12 +130,12 @@ export default class TAK extends EventEmitter {
|
|
|
127
130
|
result = TAK.findCoT(buff);
|
|
128
131
|
}
|
|
129
132
|
}).on('timeout', () => {
|
|
130
|
-
|
|
133
|
+
this.emit('timeout');
|
|
131
134
|
}).on('error', (err: Error) => {
|
|
132
135
|
this.emit('error', err);
|
|
133
136
|
}).on('end', () => {
|
|
134
137
|
this.open = false;
|
|
135
|
-
|
|
138
|
+
this.emit('end');
|
|
136
139
|
});
|
|
137
140
|
|
|
138
141
|
this.pingInterval = setInterval(() => {
|
|
@@ -234,4 +237,6 @@ export default class TAK extends EventEmitter {
|
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
|
|
237
|
-
export {
|
|
240
|
+
export {
|
|
241
|
+
CoT,
|
|
242
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import TAKAPI from '../api.js';
|
|
2
|
+
import { Type, Static } from '@sinclair/typebox';
|
|
3
|
+
|
|
4
|
+
export const Contact = Type.Object({
|
|
5
|
+
filterGroups: Type.Any(), // I'm not familiar with this one
|
|
6
|
+
notes: Type.String(),
|
|
7
|
+
callsign: Type.String(),
|
|
8
|
+
team: Type.String(),
|
|
9
|
+
role: Type.String(),
|
|
10
|
+
takv: Type.String(),
|
|
11
|
+
uid: Type.String()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default class {
|
|
15
|
+
api: TAKAPI;
|
|
16
|
+
|
|
17
|
+
constructor(api: TAKAPI) {
|
|
18
|
+
this.api = api;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async list(): Promise<Array<Static<typeof Contact>>> {
|
|
22
|
+
const url = new URL(`/Marti/api/contacts/all`, this.api.url);
|
|
23
|
+
return await this.api.fetch(url, {
|
|
24
|
+
method: 'GET'
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import TAKAPI from '../api.js';
|
|
2
|
+
import { APIAuthPassword } from '../auth.js';
|
|
3
|
+
import { Static, Type } from '@sinclair/typebox';
|
|
4
|
+
import pem from 'pem';
|
|
5
|
+
import xml2js from 'xml2js';
|
|
6
|
+
|
|
7
|
+
export const CertificateResponse = Type.Object({
|
|
8
|
+
cert: Type.String(),
|
|
9
|
+
key: Type.String()
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export default class {
|
|
13
|
+
api: TAKAPI;
|
|
14
|
+
|
|
15
|
+
constructor(api: TAKAPI) {
|
|
16
|
+
this.api = api;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async config() {
|
|
20
|
+
const url = new URL(`/Marti/api/tls/config`, this.api.url);
|
|
21
|
+
return await this.api.fetch(url, {
|
|
22
|
+
method: 'GET'
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async generate(): Promise<Static<typeof CertificateResponse>> {
|
|
27
|
+
if (!(this.api.auth instanceof APIAuthPassword)) throw new Error('Must use Password Auth');
|
|
28
|
+
|
|
29
|
+
const config = await xml2js.parseStringPromise(await this.config());
|
|
30
|
+
|
|
31
|
+
let organization = null;
|
|
32
|
+
let organizationUnit = null;
|
|
33
|
+
for (const nameEntry of config['ns2:certificateConfig'].nameEntries) {
|
|
34
|
+
for (const ne of nameEntry.nameEntry) {
|
|
35
|
+
if (ne['$'].name === 'O') organization = ne['$'].value;
|
|
36
|
+
if (ne['$'].name === 'OU') organizationUnit = ne['$'].value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const createCSR = pem.promisified.createCSR;
|
|
41
|
+
|
|
42
|
+
const keys: {
|
|
43
|
+
csr: string,
|
|
44
|
+
clientKey: string
|
|
45
|
+
} = await createCSR({
|
|
46
|
+
organization,
|
|
47
|
+
organizationUnit,
|
|
48
|
+
commonName: this.api.auth.username
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const url = new URL(`/Marti/api/tls/signClient/v2`, this.api.url);
|
|
52
|
+
url.searchParams.append('clientUid', this.api.auth.username + ' (ETL)');
|
|
53
|
+
url.searchParams.append('version', '3');
|
|
54
|
+
|
|
55
|
+
const res = await this.api.fetch(url, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
nocookies: true,
|
|
58
|
+
headers: {
|
|
59
|
+
Accept: 'application/json',
|
|
60
|
+
Authorization: 'Basic ' + btoa(this.api.auth.username + ":" + this.api.auth.password)
|
|
61
|
+
},
|
|
62
|
+
body: keys.csr
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
let cert = '-----BEGIN CERTIFICATE-----\n' + res.signedCert;
|
|
66
|
+
if (!res.signedCert.endsWith('\n')) cert = cert + '\n';
|
|
67
|
+
cert = cert + '-----END CERTIFICATE-----';
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
cert,
|
|
71
|
+
key: keys.clientKey
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import TAKAPI from '../api.js';
|
|
2
|
+
import { Type, Static } from '@sinclair/typebox';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
|
|
5
|
+
export const ExportInput = Type.Object({
|
|
6
|
+
startTime: Type.String(),
|
|
7
|
+
endTime: Type.String(),
|
|
8
|
+
groups: Type.Array(Type.String()),
|
|
9
|
+
format: Type.String({ enum: ['kmz', 'kml'] }),
|
|
10
|
+
interval: Type.Optional(Type.Number()),
|
|
11
|
+
multiTrackThreshold: Type.Optional(Type.String()),
|
|
12
|
+
extendedData: Type.Optional(Type.Boolean()),
|
|
13
|
+
optimizeExport: Type.Optional(Type.Boolean()),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @class
|
|
18
|
+
*/
|
|
19
|
+
export default class {
|
|
20
|
+
api: TAKAPI;
|
|
21
|
+
|
|
22
|
+
constructor(api: TAKAPI) {
|
|
23
|
+
this.api = api;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async export(query: Static<typeof ExportInput>): Promise<Readable> {
|
|
27
|
+
const url = new URL(`/Marti/ExportMissionKML`, this.api.url);
|
|
28
|
+
|
|
29
|
+
const params = new URLSearchParams();
|
|
30
|
+
let q: keyof Static<typeof ExportInput>;
|
|
31
|
+
for (q in query) {
|
|
32
|
+
if (query[q] !== undefined ) {
|
|
33
|
+
params.append(q, String(query[q]));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const res = await this.api.fetch(url, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: params
|
|
40
|
+
}, true);
|
|
41
|
+
|
|
42
|
+
return res.body;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/lib/api/files.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import TAKAPI from '../api.js';
|
|
2
|
+
import FormData from 'form-data';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import mime from 'mime';
|
|
5
|
+
import { Type, Static } from '@sinclair/typebox';
|
|
6
|
+
|
|
7
|
+
export const Content = Type.Object({
|
|
8
|
+
UID: Type.String(),
|
|
9
|
+
SubmissionDateTime: Type.String(),
|
|
10
|
+
Keywords: Type.Array(Type.String()),
|
|
11
|
+
MIMEType: Type.String(),
|
|
12
|
+
SubmissionUser: Type.String(),
|
|
13
|
+
PrimaryKey: Type.String(),
|
|
14
|
+
Hash: Type.String(),
|
|
15
|
+
CreatorUid: Type.String(),
|
|
16
|
+
Name: Type.String()
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const Config = Type.Object({
|
|
20
|
+
uploadSizeLimit: Type.Integer()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export default class File {
|
|
24
|
+
api: TAKAPI;
|
|
25
|
+
|
|
26
|
+
constructor(api: TAKAPI) {
|
|
27
|
+
this.api = api;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// TODO Investigate this endpoint
|
|
31
|
+
list() {
|
|
32
|
+
new URL(`/Marti/api/sync/search`, this.api.url);
|
|
33
|
+
// param hash=<hash>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async meta(hash: string): Promise<string> {
|
|
37
|
+
const url = new URL(`/Marti/sync/${encodeURIComponent(hash)}/metadata`, this.api.url);
|
|
38
|
+
|
|
39
|
+
const res = await this.api.fetch(url, {
|
|
40
|
+
method: 'GET'
|
|
41
|
+
}, true);
|
|
42
|
+
|
|
43
|
+
const body = await res.text();
|
|
44
|
+
|
|
45
|
+
return body;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async download(hash: string): Promise<Readable> {
|
|
49
|
+
const url = new URL(`/Marti/sync/content`, this.api.url);
|
|
50
|
+
url.searchParams.append('hash', hash);
|
|
51
|
+
|
|
52
|
+
const res = await this.api.fetch(url, {
|
|
53
|
+
method: 'GET'
|
|
54
|
+
}, true);
|
|
55
|
+
|
|
56
|
+
return res.body;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async adminDelete(hash: string) {
|
|
60
|
+
const url = new URL(`/Marti/api/files/${hash}`, this.api.url);
|
|
61
|
+
|
|
62
|
+
return await this.api.fetch(url, {
|
|
63
|
+
method: 'DELETE',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async delete(hash: string) {
|
|
68
|
+
const url = new URL(`/Marti/sync/delete`, this.api.url);
|
|
69
|
+
url.searchParams.append('hash', hash)
|
|
70
|
+
|
|
71
|
+
return await this.api.fetch(url, {
|
|
72
|
+
method: 'DELETE',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TODO Return a Content Object
|
|
77
|
+
async uploadPackage(opts: {
|
|
78
|
+
name: string;
|
|
79
|
+
creatorUid: string;
|
|
80
|
+
hash: string;
|
|
81
|
+
}, body: Readable | Buffer): Promise<string> {
|
|
82
|
+
const url = new URL(`/Marti/sync/missionupload`, this.api.url);
|
|
83
|
+
url.searchParams.append('filename', opts.name)
|
|
84
|
+
url.searchParams.append('creatorUid', opts.creatorUid)
|
|
85
|
+
url.searchParams.append('hash', opts.hash)
|
|
86
|
+
|
|
87
|
+
if (body instanceof Buffer) {
|
|
88
|
+
body = Readable.from(body as Buffer);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const form = new FormData()
|
|
92
|
+
form.append('assetfile', body as Readable);
|
|
93
|
+
|
|
94
|
+
const res = await this.api.fetch(url, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
body: form
|
|
97
|
+
}) as string;
|
|
98
|
+
|
|
99
|
+
return res;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async upload(opts: {
|
|
103
|
+
name: string;
|
|
104
|
+
contentLength: number;
|
|
105
|
+
contentType?: string;
|
|
106
|
+
keywords: string[];
|
|
107
|
+
creatorUid: string;
|
|
108
|
+
latitude?: string;
|
|
109
|
+
longitude?: string;
|
|
110
|
+
altitude?: string;
|
|
111
|
+
}, body: Readable | Buffer): Promise<Static<typeof Content>> {
|
|
112
|
+
const url = new URL(`/Marti/sync/upload`, this.api.url);
|
|
113
|
+
url.searchParams.append('name', opts.name)
|
|
114
|
+
url.searchParams.append('keywords', opts.keywords.join(','))
|
|
115
|
+
url.searchParams.append('creatorUid', opts.creatorUid)
|
|
116
|
+
if (opts.altitude) url.searchParams.append('altitude', opts.altitude);
|
|
117
|
+
if (opts.longitude) url.searchParams.append('longitude', opts.longitude);
|
|
118
|
+
if (opts.latitude) url.searchParams.append('latitude', opts.latitude);
|
|
119
|
+
|
|
120
|
+
if (body instanceof Buffer) {
|
|
121
|
+
body = Readable.from(body as Buffer);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const res = await this.api.fetch(url, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: {
|
|
127
|
+
'Content-Type': opts.contentType ? opts.contentType : mime.getType(opts.name),
|
|
128
|
+
'Content-Length': opts.contentLength
|
|
129
|
+
},
|
|
130
|
+
body: body as Readable
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return JSON.parse(res);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async count() {
|
|
137
|
+
const url = new URL('/Marti/api/files/metadata/count', this.api.url);
|
|
138
|
+
|
|
139
|
+
return await this.api.fetch(url, {
|
|
140
|
+
method: 'GET'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async config(): Promise<Static<typeof Config>> {
|
|
145
|
+
const url = new URL('/files/api/config', this.api.url);
|
|
146
|
+
|
|
147
|
+
return await this.api.fetch(url, {
|
|
148
|
+
method: 'GET'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Static, Type } from '@sinclair/typebox';
|
|
2
|
+
import TAKAPI from '../api.js';
|
|
3
|
+
import { TAKList } from './types.js';
|
|
4
|
+
|
|
5
|
+
export const Group = Type.Object({
|
|
6
|
+
name: Type.String(),
|
|
7
|
+
direction: Type.String(),
|
|
8
|
+
created: Type.String(),
|
|
9
|
+
type: Type.String(),
|
|
10
|
+
bitpos: Type.Number(),
|
|
11
|
+
active: Type.Boolean(),
|
|
12
|
+
description: Type.Optional(Type.String())
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export const GroupListInput = Type.Object({
|
|
16
|
+
useCache: Type.Optional(Type.Boolean())
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const TAKList_Group = TAKList(Group);
|
|
20
|
+
|
|
21
|
+
export default class {
|
|
22
|
+
api: TAKAPI;
|
|
23
|
+
|
|
24
|
+
constructor(api: TAKAPI) {
|
|
25
|
+
this.api = api;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async list(
|
|
29
|
+
query: Static<typeof GroupListInput> = {}
|
|
30
|
+
): Promise<Static<typeof TAKList_Group>> {
|
|
31
|
+
const url = new URL(`/Marti/api/groups/all`, this.api.url);
|
|
32
|
+
|
|
33
|
+
let q: keyof Static<typeof GroupListInput>;
|
|
34
|
+
for (q in query) {
|
|
35
|
+
if (query[q] !== undefined) {
|
|
36
|
+
url.searchParams.append(q, String(query[q]));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return await this.api.fetch(url, {
|
|
41
|
+
method: 'GET'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async update(
|
|
46
|
+
body: Static<typeof Group>[],
|
|
47
|
+
query: Static<typeof GroupListInput> = {}
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const url = new URL(`/Marti/api/groups/active`, this.api.url);
|
|
50
|
+
|
|
51
|
+
let q: keyof Static<typeof GroupListInput>;
|
|
52
|
+
for (q in query) {
|
|
53
|
+
if (query[q] !== undefined) {
|
|
54
|
+
url.searchParams.append(q, String(query[q]));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await this.api.fetch(url, {
|
|
59
|
+
method: 'PUT',
|
|
60
|
+
body
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|