@tiflis-io/tiflis-code-tunnel 0.3.2 → 0.3.4
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/dist/main.js +185 -16
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -372,14 +372,16 @@ function registerWatchApiRoute(app, deps) {
|
|
|
372
372
|
});
|
|
373
373
|
app.post("/api/v1/watch/command", async (request, reply) => {
|
|
374
374
|
try {
|
|
375
|
-
const { device_id, message } = request.body;
|
|
376
|
-
if (!device_id) {
|
|
375
|
+
const { tunnel_id, auth_key, device_id, message } = request.body;
|
|
376
|
+
if (!tunnel_id || !auth_key || !device_id) {
|
|
377
377
|
return await reply.status(400).send({
|
|
378
378
|
error: "missing_parameters",
|
|
379
|
-
message: "device_id and message are required"
|
|
379
|
+
message: "tunnel_id, auth_key, device_id, and message are required"
|
|
380
380
|
});
|
|
381
381
|
}
|
|
382
|
-
const sent = httpClientOperations.
|
|
382
|
+
const sent = httpClientOperations.sendCommandWithAuth({
|
|
383
|
+
tunnelId: tunnel_id,
|
|
384
|
+
authKey: auth_key,
|
|
383
385
|
deviceId: device_id,
|
|
384
386
|
message
|
|
385
387
|
});
|
|
@@ -398,16 +400,18 @@ function registerWatchApiRoute(app, deps) {
|
|
|
398
400
|
});
|
|
399
401
|
app.get("/api/v1/watch/messages", async (request, reply) => {
|
|
400
402
|
try {
|
|
401
|
-
const { device_id, since, ack } = request.query;
|
|
402
|
-
if (!device_id) {
|
|
403
|
+
const { tunnel_id, auth_key, device_id, since, ack } = request.query;
|
|
404
|
+
if (!tunnel_id || !auth_key || !device_id) {
|
|
403
405
|
return await reply.status(400).send({
|
|
404
406
|
error: "missing_parameters",
|
|
405
|
-
message: "device_id
|
|
407
|
+
message: "tunnel_id, auth_key, and device_id are required"
|
|
406
408
|
});
|
|
407
409
|
}
|
|
408
410
|
const sinceSequence = since ? parseInt(since, 10) : 0;
|
|
409
411
|
const ackSequence = ack ? parseInt(ack, 10) : void 0;
|
|
410
|
-
const result = httpClientOperations.
|
|
412
|
+
const result = httpClientOperations.pollMessagesWithAuth({
|
|
413
|
+
tunnelId: tunnel_id,
|
|
414
|
+
authKey: auth_key,
|
|
411
415
|
deviceId: device_id,
|
|
412
416
|
sinceSequence,
|
|
413
417
|
acknowledgeSequence: ackSequence
|
|
@@ -428,14 +432,16 @@ function registerWatchApiRoute(app, deps) {
|
|
|
428
432
|
});
|
|
429
433
|
app.get("/api/v1/watch/state", async (request, reply) => {
|
|
430
434
|
try {
|
|
431
|
-
const { device_id } = request.query;
|
|
432
|
-
if (!device_id) {
|
|
435
|
+
const { tunnel_id, auth_key, device_id } = request.query;
|
|
436
|
+
if (!tunnel_id || !auth_key || !device_id) {
|
|
433
437
|
return await reply.status(400).send({
|
|
434
438
|
error: "missing_parameters",
|
|
435
|
-
message: "device_id
|
|
439
|
+
message: "tunnel_id, auth_key, and device_id are required"
|
|
436
440
|
});
|
|
437
441
|
}
|
|
438
|
-
const result = httpClientOperations.
|
|
442
|
+
const result = httpClientOperations.getStateWithAuth({
|
|
443
|
+
tunnelId: tunnel_id,
|
|
444
|
+
authKey: auth_key,
|
|
439
445
|
deviceId: device_id
|
|
440
446
|
});
|
|
441
447
|
return await reply.status(200).send({
|
|
@@ -451,14 +457,18 @@ function registerWatchApiRoute(app, deps) {
|
|
|
451
457
|
});
|
|
452
458
|
app.post("/api/v1/watch/disconnect", async (request, reply) => {
|
|
453
459
|
try {
|
|
454
|
-
const { device_id } = request.body;
|
|
455
|
-
if (!device_id) {
|
|
460
|
+
const { tunnel_id, auth_key, device_id } = request.body;
|
|
461
|
+
if (!tunnel_id || !auth_key || !device_id) {
|
|
456
462
|
return await reply.status(400).send({
|
|
457
463
|
error: "missing_parameters",
|
|
458
|
-
message: "device_id
|
|
464
|
+
message: "tunnel_id, auth_key, and device_id are required"
|
|
459
465
|
});
|
|
460
466
|
}
|
|
461
|
-
const disconnected = httpClientOperations.
|
|
467
|
+
const disconnected = httpClientOperations.disconnectWithAuth({
|
|
468
|
+
tunnelId: tunnel_id,
|
|
469
|
+
authKey: auth_key,
|
|
470
|
+
deviceId: device_id
|
|
471
|
+
});
|
|
462
472
|
log.info({ deviceId: device_id }, "Watch disconnected via HTTP");
|
|
463
473
|
return await reply.status(200).send({
|
|
464
474
|
success: disconnected
|
|
@@ -1839,6 +1849,7 @@ var HttpClientOperationsUseCase = class {
|
|
|
1839
1849
|
throw new InvalidAuthKeyError();
|
|
1840
1850
|
}
|
|
1841
1851
|
let client = this.httpClientRegistry.get(deviceId);
|
|
1852
|
+
const isNewClient = !client;
|
|
1842
1853
|
if (client) {
|
|
1843
1854
|
client.recordPoll();
|
|
1844
1855
|
this.logger.info({ deviceId, tunnelId: tunnelIdStr }, "HTTP client reconnected");
|
|
@@ -1850,6 +1861,21 @@ var HttpClientOperationsUseCase = class {
|
|
|
1850
1861
|
this.httpClientRegistry.register(client);
|
|
1851
1862
|
this.logger.info({ deviceId, tunnelId: tunnelIdStr }, "HTTP client registered");
|
|
1852
1863
|
}
|
|
1864
|
+
if (workstation.isOnline) {
|
|
1865
|
+
const authMessage = {
|
|
1866
|
+
type: "auth",
|
|
1867
|
+
payload: {
|
|
1868
|
+
auth_key: authKeyStr,
|
|
1869
|
+
device_id: deviceId
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
const sent = workstation.send(JSON.stringify(authMessage));
|
|
1873
|
+
if (sent) {
|
|
1874
|
+
this.logger.debug({ deviceId, isNewClient }, "Forwarded auth to workstation for HTTP client");
|
|
1875
|
+
} else {
|
|
1876
|
+
this.logger.warn({ deviceId }, "Failed to forward auth to workstation");
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1853
1879
|
return {
|
|
1854
1880
|
success: true,
|
|
1855
1881
|
tunnelId: tunnelIdStr,
|
|
@@ -1888,6 +1914,57 @@ var HttpClientOperationsUseCase = class {
|
|
|
1888
1914
|
}
|
|
1889
1915
|
return sent;
|
|
1890
1916
|
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Sends a command from HTTP client to workstation with auth validation.
|
|
1919
|
+
* This method validates auth on every request and forwards auth to workstation
|
|
1920
|
+
* to ensure the device_id is registered.
|
|
1921
|
+
*/
|
|
1922
|
+
sendCommandWithAuth(input) {
|
|
1923
|
+
const { tunnelId: tunnelIdStr, authKey: authKeyStr, deviceId, message } = input;
|
|
1924
|
+
this.logger.info({ deviceId, tunnelId: tunnelIdStr, messageType: message.type }, "HTTP client sending command with auth");
|
|
1925
|
+
const tunnelId = TunnelId.create(tunnelIdStr);
|
|
1926
|
+
const authKey = AuthKey.create(authKeyStr);
|
|
1927
|
+
const workstation = this.workstationRegistry.get(tunnelId);
|
|
1928
|
+
if (!workstation) {
|
|
1929
|
+
this.logger.warn({ tunnelId: tunnelIdStr, deviceId }, "Workstation not found for command");
|
|
1930
|
+
throw new TunnelNotFoundError(tunnelIdStr);
|
|
1931
|
+
}
|
|
1932
|
+
if (!workstation.validateAuthKey(authKey)) {
|
|
1933
|
+
this.logger.warn({ tunnelId: tunnelIdStr, deviceId }, "Invalid auth key for command");
|
|
1934
|
+
throw new InvalidAuthKeyError();
|
|
1935
|
+
}
|
|
1936
|
+
if (!workstation.isOnline) {
|
|
1937
|
+
this.logger.warn({ deviceId, tunnelId: tunnelIdStr }, "Workstation offline");
|
|
1938
|
+
throw new WorkstationOfflineError(tunnelIdStr);
|
|
1939
|
+
}
|
|
1940
|
+
let client = this.httpClientRegistry.get(deviceId);
|
|
1941
|
+
if (!client) {
|
|
1942
|
+
client = new HttpClient({
|
|
1943
|
+
deviceId,
|
|
1944
|
+
tunnelId
|
|
1945
|
+
});
|
|
1946
|
+
this.httpClientRegistry.register(client);
|
|
1947
|
+
this.logger.info({ deviceId, tunnelId: tunnelIdStr }, "HTTP client registered on command");
|
|
1948
|
+
}
|
|
1949
|
+
client.recordPoll();
|
|
1950
|
+
const authMessage = {
|
|
1951
|
+
type: "auth",
|
|
1952
|
+
payload: {
|
|
1953
|
+
auth_key: authKeyStr,
|
|
1954
|
+
device_id: deviceId
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
workstation.send(JSON.stringify(authMessage));
|
|
1958
|
+
const enrichedMessage = {
|
|
1959
|
+
...message,
|
|
1960
|
+
device_id: deviceId
|
|
1961
|
+
};
|
|
1962
|
+
const sent = workstation.send(JSON.stringify(enrichedMessage));
|
|
1963
|
+
if (!sent) {
|
|
1964
|
+
this.logger.warn({ deviceId }, "Failed to send command to workstation");
|
|
1965
|
+
}
|
|
1966
|
+
return sent;
|
|
1967
|
+
}
|
|
1891
1968
|
/**
|
|
1892
1969
|
* Polls for messages for an HTTP client.
|
|
1893
1970
|
*/
|
|
@@ -1956,6 +2033,98 @@ var HttpClientOperationsUseCase = class {
|
|
|
1956
2033
|
this.logger.info({ deviceId }, "HTTP client disconnected");
|
|
1957
2034
|
return true;
|
|
1958
2035
|
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Polls for messages with auth validation (stateless).
|
|
2038
|
+
* Validates auth on every request.
|
|
2039
|
+
*/
|
|
2040
|
+
pollMessagesWithAuth(input) {
|
|
2041
|
+
const { tunnelId: tunnelIdStr, authKey: authKeyStr, deviceId, sinceSequence, acknowledgeSequence } = input;
|
|
2042
|
+
const tunnelId = TunnelId.create(tunnelIdStr);
|
|
2043
|
+
const authKey = AuthKey.create(authKeyStr);
|
|
2044
|
+
const workstation = this.workstationRegistry.get(tunnelId);
|
|
2045
|
+
if (!workstation) {
|
|
2046
|
+
throw new TunnelNotFoundError(tunnelIdStr);
|
|
2047
|
+
}
|
|
2048
|
+
if (!workstation.validateAuthKey(authKey)) {
|
|
2049
|
+
throw new InvalidAuthKeyError();
|
|
2050
|
+
}
|
|
2051
|
+
let client = this.httpClientRegistry.get(deviceId);
|
|
2052
|
+
if (!client) {
|
|
2053
|
+
client = new HttpClient({
|
|
2054
|
+
deviceId,
|
|
2055
|
+
tunnelId
|
|
2056
|
+
});
|
|
2057
|
+
this.httpClientRegistry.register(client);
|
|
2058
|
+
}
|
|
2059
|
+
client.recordPoll();
|
|
2060
|
+
if (acknowledgeSequence !== void 0 && acknowledgeSequence > 0) {
|
|
2061
|
+
client.acknowledgeMessages(acknowledgeSequence);
|
|
2062
|
+
}
|
|
2063
|
+
const messages = client.getMessagesSince(sinceSequence);
|
|
2064
|
+
this.logger.debug(
|
|
2065
|
+
{ deviceId, sinceSequence, messageCount: messages.length, currentSequence: client.currentSequence },
|
|
2066
|
+
"HTTP client poll with auth"
|
|
2067
|
+
);
|
|
2068
|
+
return {
|
|
2069
|
+
messages,
|
|
2070
|
+
currentSequence: client.currentSequence,
|
|
2071
|
+
workstationOnline: workstation.isOnline
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Gets current state with auth validation (stateless).
|
|
2076
|
+
*/
|
|
2077
|
+
getStateWithAuth(input) {
|
|
2078
|
+
const { tunnelId: tunnelIdStr, authKey: authKeyStr, deviceId } = input;
|
|
2079
|
+
const tunnelId = TunnelId.create(tunnelIdStr);
|
|
2080
|
+
const authKey = AuthKey.create(authKeyStr);
|
|
2081
|
+
const workstation = this.workstationRegistry.get(tunnelId);
|
|
2082
|
+
if (!workstation) {
|
|
2083
|
+
throw new TunnelNotFoundError(tunnelIdStr);
|
|
2084
|
+
}
|
|
2085
|
+
if (!workstation.validateAuthKey(authKey)) {
|
|
2086
|
+
throw new InvalidAuthKeyError();
|
|
2087
|
+
}
|
|
2088
|
+
let client = this.httpClientRegistry.get(deviceId);
|
|
2089
|
+
if (!client) {
|
|
2090
|
+
client = new HttpClient({
|
|
2091
|
+
deviceId,
|
|
2092
|
+
tunnelId
|
|
2093
|
+
});
|
|
2094
|
+
this.httpClientRegistry.register(client);
|
|
2095
|
+
}
|
|
2096
|
+
client.recordPoll();
|
|
2097
|
+
return {
|
|
2098
|
+
connected: true,
|
|
2099
|
+
workstationOnline: workstation.isOnline,
|
|
2100
|
+
workstationName: workstation.name,
|
|
2101
|
+
queueSize: client.queueSize,
|
|
2102
|
+
currentSequence: client.currentSequence
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Disconnects an HTTP client with auth validation (stateless).
|
|
2107
|
+
*/
|
|
2108
|
+
disconnectWithAuth(input) {
|
|
2109
|
+
const { tunnelId: tunnelIdStr, authKey: authKeyStr, deviceId } = input;
|
|
2110
|
+
const tunnelId = TunnelId.create(tunnelIdStr);
|
|
2111
|
+
const authKey = AuthKey.create(authKeyStr);
|
|
2112
|
+
const workstation = this.workstationRegistry.get(tunnelId);
|
|
2113
|
+
if (!workstation) {
|
|
2114
|
+
throw new TunnelNotFoundError(tunnelIdStr);
|
|
2115
|
+
}
|
|
2116
|
+
if (!workstation.validateAuthKey(authKey)) {
|
|
2117
|
+
throw new InvalidAuthKeyError();
|
|
2118
|
+
}
|
|
2119
|
+
const client = this.httpClientRegistry.get(deviceId);
|
|
2120
|
+
if (!client) {
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2123
|
+
client.markInactive();
|
|
2124
|
+
this.httpClientRegistry.unregister(deviceId);
|
|
2125
|
+
this.logger.info({ deviceId }, "HTTP client disconnected with auth");
|
|
2126
|
+
return true;
|
|
2127
|
+
}
|
|
1959
2128
|
/**
|
|
1960
2129
|
* Queues a message for all HTTP clients connected to a tunnel.
|
|
1961
2130
|
* Called when workstation sends a message.
|
package/package.json
CHANGED