@similie/hyphen-rtsp-tunnel 1.1.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/LICENSE +21 -0
- package/README.md +497 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/services/day.d.ts +3 -0
- package/dist/services/day.d.ts.map +1 -0
- package/dist/services/day.js +26 -0
- package/dist/services/day.js.map +1 -0
- package/dist/services/device-auth.d.ts +26 -0
- package/dist/services/device-auth.d.ts.map +1 -0
- package/dist/services/device-auth.js +95 -0
- package/dist/services/device-auth.js.map +1 -0
- package/dist/services/events.d.ts +29 -0
- package/dist/services/events.d.ts.map +1 -0
- package/dist/services/events.js +14 -0
- package/dist/services/events.js.map +1 -0
- package/dist/services/index.d.ts +9 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +9 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/notifier.d.ts +20 -0
- package/dist/services/notifier.d.ts.map +1 -0
- package/dist/services/notifier.js +12 -0
- package/dist/services/notifier.js.map +1 -0
- package/dist/services/rtsp-tunnel-gateway.d.ts +58 -0
- package/dist/services/rtsp-tunnel-gateway.d.ts.map +1 -0
- package/dist/services/rtsp-tunnel-gateway.js +532 -0
- package/dist/services/rtsp-tunnel-gateway.js.map +1 -0
- package/dist/services/sqs-notifier.d.ts +23 -0
- package/dist/services/sqs-notifier.d.ts.map +1 -0
- package/dist/services/sqs-notifier.js +109 -0
- package/dist/services/sqs-notifier.js.map +1 -0
- package/dist/services/storage-s3.d.ts +12 -0
- package/dist/services/storage-s3.d.ts.map +1 -0
- package/dist/services/storage-s3.js +49 -0
- package/dist/services/storage-s3.js.map +1 -0
- package/dist/services/storage-worker.d.ts +22 -0
- package/dist/services/storage-worker.d.ts.map +1 -0
- package/dist/services/storage-worker.js +100 -0
- package/dist/services/storage-worker.js.map +1 -0
- package/dist/services/storage.d.ts +28 -0
- package/dist/services/storage.d.ts.map +1 -0
- package/dist/services/storage.js +41 -0
- package/dist/services/storage.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { SQSClient, SendMessageCommand, } from "@aws-sdk/client-sqs";
|
|
2
|
+
/**
|
|
3
|
+
* Env:
|
|
4
|
+
* - SQS_QUEUE_URL (required)
|
|
5
|
+
* - AWS_REGION (required)
|
|
6
|
+
*
|
|
7
|
+
* Credentials:
|
|
8
|
+
* - resolved automatically by AWS SDK (env / instance role / task role / etc.)
|
|
9
|
+
*/
|
|
10
|
+
export class SqsNotifier {
|
|
11
|
+
enabled;
|
|
12
|
+
sqs = null;
|
|
13
|
+
queueUrl = null;
|
|
14
|
+
isFifo = false;
|
|
15
|
+
ctx = null;
|
|
16
|
+
constructor() {
|
|
17
|
+
const queueUrl = process.env.SQS_QUEUE_URL?.trim();
|
|
18
|
+
const region = process.env.AWS_REGION?.trim();
|
|
19
|
+
if (!queueUrl || !region) {
|
|
20
|
+
this.enabled = false;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.enabled = true;
|
|
24
|
+
this.queueUrl = queueUrl;
|
|
25
|
+
this.isFifo = queueUrl.endsWith(".fifo");
|
|
26
|
+
this.sqs = new SQSClient({ region });
|
|
27
|
+
}
|
|
28
|
+
init(ctx) {
|
|
29
|
+
this.ctx = ctx;
|
|
30
|
+
if (!this.enabled) {
|
|
31
|
+
return this.ctx.log("[rtsp-tunnel][sqs] disabled (missing SQS_QUEUE_URL or AWS_REGION)");
|
|
32
|
+
}
|
|
33
|
+
this.ctx.log("[rtsp-tunnel][sqs] enabled queue=", {
|
|
34
|
+
queueUrl: this.queueUrl,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async shutdown() {
|
|
38
|
+
// AWS SDK v3 clients have destroy() to close sockets
|
|
39
|
+
try {
|
|
40
|
+
this.sqs?.destroy();
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
this.sqs = null;
|
|
44
|
+
}
|
|
45
|
+
async send(eventName, payload) {
|
|
46
|
+
this.ctx?.log("[rtsp-tunnel][sqs] send called", { eventName, payload });
|
|
47
|
+
if (!this.enabled || !this.sqs || !this.queueUrl)
|
|
48
|
+
return;
|
|
49
|
+
// Build a consistent message envelope
|
|
50
|
+
const msg = this.buildMessage(eventName, payload);
|
|
51
|
+
const params = {
|
|
52
|
+
QueueUrl: this.queueUrl,
|
|
53
|
+
MessageBody: JSON.stringify(msg),
|
|
54
|
+
};
|
|
55
|
+
// FIFO-safe handling
|
|
56
|
+
if (this.isFifo) {
|
|
57
|
+
params.MessageDeduplicationId = msg.deduplicationId;
|
|
58
|
+
params.MessageGroupId = msg.groupId;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
await this.sqs.send(new SendMessageCommand(params));
|
|
62
|
+
this.ctx?.log("[rtsp-tunnel][sqs] sent", {
|
|
63
|
+
eventName,
|
|
64
|
+
groupId: msg.groupId,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
this.ctx?.log("[rtsp-tunnel][sqs] send failed", {
|
|
69
|
+
eventName,
|
|
70
|
+
error: err?.message ?? err,
|
|
71
|
+
});
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ----------------- internals -----------------
|
|
76
|
+
buildMessage(eventName, payload) {
|
|
77
|
+
// Prefer payloadId for grouping, then deviceId.
|
|
78
|
+
const groupId = payload.payloadId || payload.deviceId || "hyphen";
|
|
79
|
+
// Dedup: prefer payloadId if present; otherwise sessionId; otherwise a stable fallback.
|
|
80
|
+
// (SQS FIFO requires <= 128 chars; we keep it simple.)
|
|
81
|
+
const deduplicationId = payload.payloadId ||
|
|
82
|
+
payload.sessionId ||
|
|
83
|
+
`${payload.deviceId || "unknown"}:${eventName}:${payload.capturedAt || payload.at || ""}`;
|
|
84
|
+
// For stored snapshots, include storedUri if your storage worker attaches it.
|
|
85
|
+
// For now, it may be undefined and that’s fine.
|
|
86
|
+
const storedUri = payload.storedUri;
|
|
87
|
+
return {
|
|
88
|
+
version: 1,
|
|
89
|
+
source: "hyphen-rtsp-tunnel",
|
|
90
|
+
event: eventName,
|
|
91
|
+
capturedAt: payload.capturedAt || null,
|
|
92
|
+
at: payload.at || null,
|
|
93
|
+
deviceId: payload.deviceId || "unknown",
|
|
94
|
+
payloadId: payload.payloadId ?? null,
|
|
95
|
+
sessionId: payload.sessionId || null,
|
|
96
|
+
remote: payload.remote || null,
|
|
97
|
+
// file paths/uris
|
|
98
|
+
localPath: payload.localPath || null,
|
|
99
|
+
storedUri: storedUri || null,
|
|
100
|
+
// failure details (for snapshot:failed)
|
|
101
|
+
stage: payload.stage || null,
|
|
102
|
+
error: payload.error || null,
|
|
103
|
+
// FIFO helpers
|
|
104
|
+
groupId,
|
|
105
|
+
deduplicationId,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=sqs-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqs-notifier.js","sourceRoot":"","sources":["../../src/services/sqs-notifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StorageAdapter, StoreInput, StoreResult } from "./storage.js";
|
|
2
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
3
|
+
export declare class S3StorageAdapter implements StorageAdapter {
|
|
4
|
+
private readonly bucket;
|
|
5
|
+
private readonly prefix;
|
|
6
|
+
private readonly deleteOnMove;
|
|
7
|
+
name: string;
|
|
8
|
+
private readonly s3;
|
|
9
|
+
constructor(bucket: string, prefix?: string, s3Client?: S3Client, deleteOnMove?: boolean);
|
|
10
|
+
store(input: StoreInput): Promise<StoreResult>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=storage-s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-s3.d.ts","sourceRoot":"","sources":["../../src/services/storage-s3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,QAAQ,EAAoB,MAAM,oBAAoB,CAAC;AAMhE,qBAAa,gBAAiB,YAAW,cAAc;IAKnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAP/B,IAAI,SAAQ;IACZ,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;gBAGX,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAsB,EAC/C,QAAQ,CAAC,EAAE,QAAQ,EACF,YAAY,GAAE,OAAc;IAMzC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CAmCrD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
|
|
4
|
+
function safeSegment(s) {
|
|
5
|
+
return (s || "unknown").replace(/[^a-zA-Z0-9._/-]/g, "_").slice(0, 128);
|
|
6
|
+
}
|
|
7
|
+
export class S3StorageAdapter {
|
|
8
|
+
bucket;
|
|
9
|
+
prefix;
|
|
10
|
+
deleteOnMove;
|
|
11
|
+
name = "s3";
|
|
12
|
+
s3;
|
|
13
|
+
constructor(bucket, prefix = "hyphen/rtsp", s3Client, deleteOnMove = true) {
|
|
14
|
+
this.bucket = bucket;
|
|
15
|
+
this.prefix = prefix;
|
|
16
|
+
this.deleteOnMove = deleteOnMove;
|
|
17
|
+
this.s3 = s3Client ?? new S3Client({});
|
|
18
|
+
if (!bucket)
|
|
19
|
+
throw new Error("S3StorageAdapter requires bucket");
|
|
20
|
+
}
|
|
21
|
+
async store(input) {
|
|
22
|
+
const fileName = path.basename(input.localPath);
|
|
23
|
+
const day = input.day ?? new Date(input.capturedAt).toISOString().slice(0, 10); // "2025-12-28"
|
|
24
|
+
const keyParts = [
|
|
25
|
+
safeSegment(this.prefix),
|
|
26
|
+
safeSegment(input.deviceId),
|
|
27
|
+
input.payloadId ? safeSegment(input.payloadId) : null,
|
|
28
|
+
// safeSegment(input.capturedAt.replace(/[:.]/g, "-")),
|
|
29
|
+
day,
|
|
30
|
+
fileName,
|
|
31
|
+
].filter(Boolean);
|
|
32
|
+
const key = keyParts.join("/");
|
|
33
|
+
const body = fs.createReadStream(input.localPath);
|
|
34
|
+
await this.s3.send(new PutObjectCommand({
|
|
35
|
+
Bucket: this.bucket,
|
|
36
|
+
Key: key,
|
|
37
|
+
Body: body,
|
|
38
|
+
ContentType: "image/jpeg",
|
|
39
|
+
}));
|
|
40
|
+
if (this.deleteOnMove) {
|
|
41
|
+
fs.unlinkSync(input.localPath);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
storage: "s3",
|
|
45
|
+
storedUri: `s3://${this.bucket}/${key}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=storage-s3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-s3.js","sourceRoot":"","sources":["../../src/services/storage-s3.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,OAAO,gBAAgB;IAKR;IACA;IAEA;IAPnB,IAAI,GAAG,IAAI,CAAC;IACK,EAAE,CAAW;IAE9B,YACmB,MAAc,EACd,SAAiB,aAAa,EAC/C,QAAmB,EACF,eAAwB,IAAI;QAH5B,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAwB;QAE9B,iBAAY,GAAZ,YAAY,CAAgB;QAE7C,IAAI,CAAC,EAAE,GAAG,QAAQ,IAAI,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAiB;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,GAAG,GACP,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe;QACrF,MAAM,QAAQ,GAAG;YACf,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;YACxB,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;YAC3B,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI;YACrD,yDAAyD;YACzD,GAAG;YACH,QAAQ;SACT,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAElB,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE/B,MAAM,IAAI,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAChB,IAAI,gBAAgB,CAAC;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,GAAG,EAAE,GAAG;YACR,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,YAAY;SAC1B,CAAC,CACH,CAAC;QAEF,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,QAAQ,IAAI,CAAC,MAAM,IAAI,GAAG,EAAE;SACxC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RtspTunnelEvents } from "./events.js";
|
|
2
|
+
import type { StorageAdapter } from "./storage.js";
|
|
3
|
+
type CtxLike = {
|
|
4
|
+
log: (...args: any[]) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare class StorageWorker {
|
|
7
|
+
private readonly ctx;
|
|
8
|
+
private readonly events;
|
|
9
|
+
private readonly storage;
|
|
10
|
+
private readonly concurrency;
|
|
11
|
+
private readonly deleteAfterStore;
|
|
12
|
+
private running;
|
|
13
|
+
private inFlight;
|
|
14
|
+
private queue;
|
|
15
|
+
private onCapturedBound;
|
|
16
|
+
constructor(ctx: CtxLike, events: RtspTunnelEvents, storage: StorageAdapter, concurrency?: number, deleteAfterStore?: boolean);
|
|
17
|
+
start(): void;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
private pump;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=storage-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-worker.d.ts","sourceRoot":"","sources":["../../src/services/storage-worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,KAAK,OAAO,GAAG;IAAE,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAAE,CAAC;AAEjD,qBAAa,aAAa;IAOtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAG5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAZnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,eAAe,CAAqD;gBAGzD,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,cAAc,EACvB,WAAW,SAE3B,EACgB,gBAAgB,UACnB;IAGhB,KAAK;IAgBC,IAAI;IAmBV,OAAO,CAAC,IAAI;CAqDb"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// storage-worker.ts
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { dayFromCapturedAt } from "./day.js";
|
|
4
|
+
export class StorageWorker {
|
|
5
|
+
ctx;
|
|
6
|
+
events;
|
|
7
|
+
storage;
|
|
8
|
+
concurrency;
|
|
9
|
+
deleteAfterStore;
|
|
10
|
+
running = false;
|
|
11
|
+
inFlight = 0;
|
|
12
|
+
queue = [];
|
|
13
|
+
onCapturedBound = null;
|
|
14
|
+
constructor(ctx, events, storage, concurrency = Number(process.env.STORAGE_CONCURRENCY ?? "2"), deleteAfterStore = (process.env.STORAGE_DELETE_LOCAL ??
|
|
15
|
+
"1") === "1") {
|
|
16
|
+
this.ctx = ctx;
|
|
17
|
+
this.events = events;
|
|
18
|
+
this.storage = storage;
|
|
19
|
+
this.concurrency = concurrency;
|
|
20
|
+
this.deleteAfterStore = deleteAfterStore;
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
if (this.running)
|
|
24
|
+
return;
|
|
25
|
+
this.running = true;
|
|
26
|
+
this.onCapturedBound = (e) => {
|
|
27
|
+
// cheap enqueue; never await here
|
|
28
|
+
this.queue.push(e);
|
|
29
|
+
this.pump();
|
|
30
|
+
};
|
|
31
|
+
this.events.on("snapshot:captured", this.onCapturedBound);
|
|
32
|
+
this.ctx.log(`[rtsp-tunnel] storage worker started storage=${this.storage.name} concurrency=${this.concurrency}`);
|
|
33
|
+
}
|
|
34
|
+
async stop() {
|
|
35
|
+
if (!this.running)
|
|
36
|
+
return;
|
|
37
|
+
this.running = false;
|
|
38
|
+
if (this.onCapturedBound) {
|
|
39
|
+
this.events.off("snapshot:captured", this.onCapturedBound);
|
|
40
|
+
this.onCapturedBound = null;
|
|
41
|
+
}
|
|
42
|
+
// wait a short window for in-flight tasks (best effort)
|
|
43
|
+
const start = Date.now();
|
|
44
|
+
while (this.inFlight > 0 && Date.now() - start < 5000) {
|
|
45
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
46
|
+
}
|
|
47
|
+
this.queue = [];
|
|
48
|
+
this.ctx.log("[rtsp-tunnel] storage worker stopped");
|
|
49
|
+
}
|
|
50
|
+
pump() {
|
|
51
|
+
if (!this.running)
|
|
52
|
+
return;
|
|
53
|
+
while (this.inFlight < this.concurrency && this.queue.length > 0) {
|
|
54
|
+
const job = this.queue.shift();
|
|
55
|
+
this.inFlight++;
|
|
56
|
+
(async () => {
|
|
57
|
+
try {
|
|
58
|
+
const day = dayFromCapturedAt(job.capturedAt, job.tzOffsetHours ?? null);
|
|
59
|
+
const stored = await this.storage.store({
|
|
60
|
+
localPath: job.localPath,
|
|
61
|
+
deviceId: job.deviceId,
|
|
62
|
+
payloadId: job.payloadId,
|
|
63
|
+
capturedAt: job.capturedAt,
|
|
64
|
+
day,
|
|
65
|
+
});
|
|
66
|
+
// delete local file by default (configurable), unless adapter says otherwise
|
|
67
|
+
const shouldDelete = this.deleteAfterStore && (stored.deleteLocal ?? true);
|
|
68
|
+
if (shouldDelete) {
|
|
69
|
+
try {
|
|
70
|
+
fs.unlinkSync(job.localPath);
|
|
71
|
+
}
|
|
72
|
+
catch { }
|
|
73
|
+
}
|
|
74
|
+
this.events.emitStored({
|
|
75
|
+
...job,
|
|
76
|
+
storage: stored.storage,
|
|
77
|
+
storedUri: stored.storedUri,
|
|
78
|
+
day,
|
|
79
|
+
tzOffsetHours: job.tzOffsetHours ?? null,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
this.events.emitFailed({
|
|
84
|
+
sessionId: job.sessionId,
|
|
85
|
+
deviceId: job.deviceId,
|
|
86
|
+
payloadId: job.payloadId,
|
|
87
|
+
remote: job.remote,
|
|
88
|
+
stage: "store",
|
|
89
|
+
error: e?.message ?? String(e),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
this.inFlight--;
|
|
94
|
+
this.pump();
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=storage-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-worker.js","sourceRoot":"","sources":["../../src/services/storage-worker.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,OAAO,aAAa;IAOL;IACA;IACA;IACA;IAGA;IAZX,OAAO,GAAG,KAAK,CAAC;IAChB,QAAQ,GAAG,CAAC,CAAC;IACb,KAAK,GAA4B,EAAE,CAAC;IACpC,eAAe,GAAgD,IAAI,CAAC;IAE5E,YACmB,GAAY,EACZ,MAAwB,EACxB,OAAuB,EACvB,cAAc,MAAM,CACnC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,GAAG,CACvC,EACgB,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB;QACnE,GAAG,CAAC,KAAK,GAAG;QAPG,QAAG,GAAH,GAAG,CAAS;QACZ,WAAM,GAAN,MAAM,CAAkB;QACxB,YAAO,GAAP,OAAO,CAAgB;QACvB,gBAAW,GAAX,WAAW,CAE3B;QACgB,qBAAgB,GAAhB,gBAAgB,CACnB;IACb,CAAC;IAEJ,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,EAAE;YAC3B,kCAAkC;YAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,GAAG,CACV,gDAAgD,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,IAAI,CAAC,WAAW,EAAE,CACpG,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YAC3D,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC;QAED,wDAAwD;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACvD,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,CAAC;YAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAEhB,CAAC,KAAK,IAAI,EAAE;gBACV,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,iBAAiB,CAC3B,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,aAAa,IAAI,IAAI,CAC1B,CAAC;oBACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;wBACtC,SAAS,EAAE,GAAG,CAAC,SAAS;wBACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;wBACtB,SAAS,EAAE,GAAG,CAAC,SAAS;wBACxB,UAAU,EAAE,GAAG,CAAC,UAAU;wBAC1B,GAAG;qBACJ,CAAC,CAAC;oBAEH,6EAA6E;oBAC7E,MAAM,YAAY,GAChB,IAAI,CAAC,gBAAgB,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;oBACxD,IAAI,YAAY,EAAE,CAAC;wBACjB,IAAI,CAAC;4BACH,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;wBAC/B,CAAC;wBAAC,MAAM,CAAC,CAAA,CAAC;oBACZ,CAAC;oBAED,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;wBACrB,GAAG,GAAG;wBACN,OAAO,EAAE,MAAM,CAAC,OAAO;wBACvB,SAAS,EAAE,MAAM,CAAC,SAAS;wBAC3B,GAAG;wBACH,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI;qBACzC,CAAC,CAAC;gBACL,CAAC;gBAAC,OAAO,CAAM,EAAE,CAAC;oBAChB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;wBACrB,SAAS,EAAE,GAAG,CAAC,SAAS;wBACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;wBACtB,SAAS,EAAE,GAAG,CAAC,SAAS;wBACxB,MAAM,EAAE,GAAG,CAAC,MAAM;wBAClB,KAAK,EAAE,OAAO;wBACd,KAAK,EAAE,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC;qBAC/B,CAAC,CAAC;gBACL,CAAC;wBAAS,CAAC;oBACT,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAChB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type StoreInput = {
|
|
2
|
+
localPath: string;
|
|
3
|
+
deviceId: string;
|
|
4
|
+
payloadId: string | null;
|
|
5
|
+
capturedAt: string;
|
|
6
|
+
day?: string;
|
|
7
|
+
};
|
|
8
|
+
export type StoreResult = {
|
|
9
|
+
storage: string;
|
|
10
|
+
storedUri: string;
|
|
11
|
+
deleteLocal?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export interface StorageAdapter {
|
|
14
|
+
name: string;
|
|
15
|
+
store(input: StoreInput): Promise<StoreResult>;
|
|
16
|
+
}
|
|
17
|
+
export declare class LocalStorageAdapter implements StorageAdapter {
|
|
18
|
+
name: string;
|
|
19
|
+
store(input: StoreInput): Promise<StoreResult>;
|
|
20
|
+
}
|
|
21
|
+
export declare class LocalMoveStorageAdapter implements StorageAdapter {
|
|
22
|
+
private readonly rootDir;
|
|
23
|
+
private readonly mode;
|
|
24
|
+
name: string;
|
|
25
|
+
constructor(rootDir: string, mode?: "copy" | "move");
|
|
26
|
+
store(input: StoreInput): Promise<StoreResult>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/services/storage.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAChD;AAGD,qBAAa,mBAAoB,YAAW,cAAc;IACxD,IAAI,SAAW;IACT,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CAOrD;AAGD,qBAAa,uBAAwB,YAAW,cAAc;IAG1D,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHvB,IAAI,SAAgB;gBAED,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,GAAG,MAAe;IAK3C,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;CAiBrD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// storage.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
// -------- Default: keep local file in place (no-op) --------
|
|
5
|
+
export class LocalStorageAdapter {
|
|
6
|
+
name = "local";
|
|
7
|
+
async store(input) {
|
|
8
|
+
return {
|
|
9
|
+
storage: "local",
|
|
10
|
+
storedUri: `file://${input.localPath}`,
|
|
11
|
+
deleteLocal: false,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// -------- Optional: move/copy to a new root folder --------
|
|
16
|
+
export class LocalMoveStorageAdapter {
|
|
17
|
+
rootDir;
|
|
18
|
+
mode;
|
|
19
|
+
name = "local-move";
|
|
20
|
+
constructor(rootDir, mode = "move") {
|
|
21
|
+
this.rootDir = rootDir;
|
|
22
|
+
this.mode = mode;
|
|
23
|
+
fs.mkdirSync(rootDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
async store(input) {
|
|
26
|
+
const safeDev = input.deviceId;
|
|
27
|
+
const sub = input.payloadId ? path.join(safeDev, input.payloadId) : safeDev;
|
|
28
|
+
const destDir = path.join(this.rootDir, sub);
|
|
29
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
30
|
+
const filename = path.basename(input.localPath);
|
|
31
|
+
const destPath = path.join(destDir, filename);
|
|
32
|
+
if (this.mode === "move") {
|
|
33
|
+
fs.renameSync(input.localPath, destPath);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
fs.copyFileSync(input.localPath, destPath);
|
|
37
|
+
}
|
|
38
|
+
return { storage: this.name, storedUri: `file://${destPath}` };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/services/storage.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAqBzB,8DAA8D;AAC9D,MAAM,OAAO,mBAAmB;IAC9B,IAAI,GAAG,OAAO,CAAC;IACf,KAAK,CAAC,KAAK,CAAC,KAAiB;QAC3B,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,SAAS,EAAE,UAAU,KAAK,CAAC,SAAS,EAAE;YACtC,WAAW,EAAE,KAAK;SACnB,CAAC;IACJ,CAAC;CACF;AAED,6DAA6D;AAC7D,MAAM,OAAO,uBAAuB;IAGf;IACA;IAHnB,IAAI,GAAG,YAAY,CAAC;IACpB,YACmB,OAAe,EACf,OAAwB,MAAM;QAD9B,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAA0B;QAE/C,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAiB;QAC3B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7C,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAE9C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,QAAQ,EAAE,EAAE,CAAC;IACjE,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@similie/hyphen-rtsp-tunnel",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "API module for Hyphen Command Center. Provides REST and WebSocket interfaces for managing RTSP streams and related services.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "adam.smith@similie.org",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"dev": "tsc -p tsconfig.json -w",
|
|
22
|
+
"prepare": "pnpm run build",
|
|
23
|
+
"docs": "./node_modules/typedoc/bin/typedoc --out docs src",
|
|
24
|
+
"release": "npm run build && standard-version"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@aws-sdk/client-s3": "^3.958.0",
|
|
28
|
+
"@aws-sdk/client-sqs": "^3.958.0",
|
|
29
|
+
"@similie/ellipsies": "^1.0.16",
|
|
30
|
+
"@similie/hyphen-command-server-types": "^1.0.6",
|
|
31
|
+
"ws": "^8.18.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^24.9.2",
|
|
35
|
+
"@types/ws": "^8.18.1",
|
|
36
|
+
"standard-version": "^9.5.0",
|
|
37
|
+
"ts-node": "^10.9.2",
|
|
38
|
+
"tsx": "^4.20.6",
|
|
39
|
+
"typedoc": "^0.28.14",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"package.json",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
]
|
|
48
|
+
}
|