forge-remote 0.1.13 → 0.1.15
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/firestore.rules +8 -0
- package/package.json +2 -9
- package/src/cli.js +62 -0
- package/src/firebase.js +5 -0
- package/src/init.js +1 -1
- package/src/notifications.js +202 -0
- package/src/session-manager.js +71 -3
- package/src/update-checker.js +72 -0
- package/src/webhook-server.js +714 -0
- package/src/webhook-watcher.js +111 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Forge Remote Relay — Webhook Config Watcher
|
|
2
|
+
// Copyright (c) 2025-2026 Iron Forge Apps
|
|
3
|
+
// Created by Daniel Wendel, CEO/Founder of Iron Forge Apps
|
|
4
|
+
|
|
5
|
+
import { getDb } from "./firebase.js";
|
|
6
|
+
import * as log from "./logger.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* In-memory cache of webhook configurations.
|
|
10
|
+
* Map<webhookId, config>
|
|
11
|
+
*/
|
|
12
|
+
const webhookConfigs = new Map();
|
|
13
|
+
|
|
14
|
+
/** Firestore unsubscribe function. */
|
|
15
|
+
let unsubscribe = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Watch Firestore for webhook config changes and maintain an in-memory cache.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} desktopId
|
|
21
|
+
* @param {string} tunnelBaseUrl — Public tunnel URL to build webhook URLs from
|
|
22
|
+
*/
|
|
23
|
+
export function watchWebhookConfigs(desktopId, tunnelBaseUrl) {
|
|
24
|
+
const db = getDb();
|
|
25
|
+
const webhooksRef = db
|
|
26
|
+
.collection("desktops")
|
|
27
|
+
.doc(desktopId)
|
|
28
|
+
.collection("webhooks");
|
|
29
|
+
|
|
30
|
+
unsubscribe = webhooksRef.onSnapshot(
|
|
31
|
+
(snap) => {
|
|
32
|
+
for (const change of snap.docChanges()) {
|
|
33
|
+
const id = change.doc.id;
|
|
34
|
+
const data = change.doc.data();
|
|
35
|
+
|
|
36
|
+
if (change.type === "removed") {
|
|
37
|
+
webhookConfigs.delete(id);
|
|
38
|
+
log.info(`Webhook removed: ${id}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// added or modified
|
|
43
|
+
webhookConfigs.set(id, data);
|
|
44
|
+
|
|
45
|
+
if (change.type === "added") {
|
|
46
|
+
log.info(
|
|
47
|
+
`Webhook registered: ${id} (source: ${data.source || "custom"}, enabled: ${data.enabled !== false})`,
|
|
48
|
+
);
|
|
49
|
+
} else {
|
|
50
|
+
log.info(`Webhook updated: ${id}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// If the webhook doc doesn't have a webhookUrl yet, write it back.
|
|
54
|
+
if (!data.webhookUrl) {
|
|
55
|
+
const webhookUrl = `${tunnelBaseUrl}/hooks/${id}`;
|
|
56
|
+
webhooksRef
|
|
57
|
+
.doc(id)
|
|
58
|
+
.update({ webhookUrl })
|
|
59
|
+
.then(() => {
|
|
60
|
+
log.info(`Wrote webhook URL for ${id}: ${webhookUrl}`);
|
|
61
|
+
})
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
log.error(
|
|
64
|
+
`Failed to write webhook URL for ${id}: ${err.message}`,
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
(err) => {
|
|
71
|
+
log.error(`Webhook watcher error: ${err.message}`);
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
log.info(`Watching webhook configs for desktop ${desktopId}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a webhook config from the in-memory cache.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} webhookId
|
|
82
|
+
* @returns {object|null}
|
|
83
|
+
*/
|
|
84
|
+
export function getWebhookConfig(webhookId) {
|
|
85
|
+
return webhookConfigs.get(webhookId) || null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the count of enabled webhooks.
|
|
90
|
+
*
|
|
91
|
+
* @returns {number}
|
|
92
|
+
*/
|
|
93
|
+
export function getActiveWebhookCount() {
|
|
94
|
+
let count = 0;
|
|
95
|
+
for (const config of webhookConfigs.values()) {
|
|
96
|
+
if (config.enabled !== false) count++;
|
|
97
|
+
}
|
|
98
|
+
return count;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Stop watching Firestore for webhook changes.
|
|
103
|
+
*/
|
|
104
|
+
export function stopWatching() {
|
|
105
|
+
if (unsubscribe) {
|
|
106
|
+
unsubscribe();
|
|
107
|
+
unsubscribe = null;
|
|
108
|
+
log.info("Stopped watching webhook configs");
|
|
109
|
+
}
|
|
110
|
+
webhookConfigs.clear();
|
|
111
|
+
}
|