@attraccess/plugin-wago 0.1.0-nightly.925.2
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/index.js +743 -0
- package/dist/migrations.js +78 -0
- package/frontend/__federation_expose_Plugin-CXB18jsp.js +1489 -0
- package/frontend/__federation_fn_import-DAGm_xmC.js +2 -0
- package/frontend/_virtual___federation_fn_import-BXODRNto.js +261 -0
- package/frontend/index.html +8 -0
- package/frontend/jsx-runtime-B9Qmn8k7.js +42 -0
- package/frontend/preload-helper-DxWiKyOd.js +68 -0
- package/frontend/react-D5kF5LxZ.js +383 -0
- package/frontend/remoteEntry.js +74 -0
- package/frontend/rolldown-runtime-EAsCxrDo.js +14 -0
- package/frontend/style.css +192 -0
- package/package.json +46 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
28
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
29
|
+
|
|
30
|
+
// backend/plugin.ts
|
|
31
|
+
var plugin_exports = {};
|
|
32
|
+
__export(plugin_exports, {
|
|
33
|
+
default: () => plugin_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(plugin_exports);
|
|
36
|
+
|
|
37
|
+
// backend/wago.controller.ts
|
|
38
|
+
var import_common2 = require("@nestjs/common");
|
|
39
|
+
var import_plugins_backend_sdk4 = require("@attraccess/plugins-backend-sdk");
|
|
40
|
+
|
|
41
|
+
// backend/wago.service.ts
|
|
42
|
+
var import_node_crypto = require("node:crypto");
|
|
43
|
+
var import_common = require("@nestjs/common");
|
|
44
|
+
|
|
45
|
+
// backend/protocol.ts
|
|
46
|
+
var DISCOVERY_ROOT = "attraccess/wago/discovery";
|
|
47
|
+
var REQUIRED_CAPABILITIES = ["claim", "heartbeat"];
|
|
48
|
+
var SUPPORTED_PROTOCOL_MAJOR = 1;
|
|
49
|
+
function parseAnnouncement(payload) {
|
|
50
|
+
let value;
|
|
51
|
+
try {
|
|
52
|
+
value = JSON.parse(payload.toString("utf8"));
|
|
53
|
+
} catch {
|
|
54
|
+
throw new Error("announcement is not valid JSON");
|
|
55
|
+
}
|
|
56
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("announcement must be an object");
|
|
57
|
+
const input = value;
|
|
58
|
+
const required = ["hardwareId", "pairingCode", "protocolVersion", "runtimeVersion"];
|
|
59
|
+
for (const key of required)
|
|
60
|
+
if (typeof input[key] !== "string" || !input[key].trim()) throw new Error(`announcement ${key} is required`);
|
|
61
|
+
if (!Array.isArray(input.capabilities) || input.capabilities.some((item) => typeof item !== "string"))
|
|
62
|
+
throw new Error("announcement capabilities must be an array of strings");
|
|
63
|
+
if (input.sequence !== void 0 && (!Number.isSafeInteger(input.sequence) || input.sequence < 0))
|
|
64
|
+
throw new Error("announcement sequence must be a non-negative integer");
|
|
65
|
+
return {
|
|
66
|
+
hardwareId: input.hardwareId.trim(),
|
|
67
|
+
pairingCode: input.pairingCode.trim(),
|
|
68
|
+
enrollmentSecret: typeof input.enrollmentSecret === "string" ? input.enrollmentSecret.trim() : void 0,
|
|
69
|
+
fingerprint: typeof input.fingerprint === "string" ? input.fingerprint.trim() : void 0,
|
|
70
|
+
protocolVersion: input.protocolVersion.trim(),
|
|
71
|
+
runtimeVersion: input.runtimeVersion.trim(),
|
|
72
|
+
capabilities: input.capabilities,
|
|
73
|
+
sequence: input.sequence
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function compatibilityError(announcement) {
|
|
77
|
+
const major = Number(announcement.protocolVersion.split(".")[0]);
|
|
78
|
+
if (!Number.isInteger(major))
|
|
79
|
+
return `Protocol version "${announcement.protocolVersion}" is invalid; install a CC100 runtime using protocol ${SUPPORTED_PROTOCOL_MAJOR}.x.`;
|
|
80
|
+
if (major !== SUPPORTED_PROTOCOL_MAJOR)
|
|
81
|
+
return `Protocol ${announcement.protocolVersion} is incompatible; this plugin supports protocol ${SUPPORTED_PROTOCOL_MAJOR}.x.`;
|
|
82
|
+
const missing = REQUIRED_CAPABILITIES.filter((capability) => !announcement.capabilities.includes(capability));
|
|
83
|
+
return missing.length ? `Controller is missing required capabilities: ${missing.join(", ")}. Update the CC100 runtime.` : null;
|
|
84
|
+
}
|
|
85
|
+
function discoveryTopic(hardwareId) {
|
|
86
|
+
return `${DISCOVERY_ROOT}/${hardwareId}`;
|
|
87
|
+
}
|
|
88
|
+
function heartbeatTopic(hardwareId) {
|
|
89
|
+
return `attraccess/wago/controllers/${hardwareId}/heartbeat`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// backend/wago-controller.entity.ts
|
|
93
|
+
var import_plugins_backend_sdk = require("@attraccess/plugins-backend-sdk");
|
|
94
|
+
var WagoController = class {
|
|
95
|
+
constructor() {
|
|
96
|
+
__publicField(this, "id");
|
|
97
|
+
__publicField(this, "hardwareId");
|
|
98
|
+
__publicField(this, "trustState");
|
|
99
|
+
__publicField(this, "name");
|
|
100
|
+
__publicField(this, "mqttServerId");
|
|
101
|
+
__publicField(this, "enrollmentId");
|
|
102
|
+
__publicField(this, "pairingCodeHash");
|
|
103
|
+
__publicField(this, "fingerprint");
|
|
104
|
+
__publicField(this, "protocolVersion");
|
|
105
|
+
__publicField(this, "runtimeVersion");
|
|
106
|
+
__publicField(this, "capabilities");
|
|
107
|
+
__publicField(this, "lastSequence");
|
|
108
|
+
__publicField(this, "lastHeartbeatAt");
|
|
109
|
+
__publicField(this, "lastSeenAt");
|
|
110
|
+
__publicField(this, "compatibilityError");
|
|
111
|
+
__publicField(this, "createdAt");
|
|
112
|
+
__publicField(this, "updatedAt");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
__decorateClass([
|
|
116
|
+
(0, import_plugins_backend_sdk.PrimaryGeneratedColumn)({ type: "integer" })
|
|
117
|
+
], WagoController.prototype, "id", 2);
|
|
118
|
+
__decorateClass([
|
|
119
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "hardware_id", unique: true })
|
|
120
|
+
], WagoController.prototype, "hardwareId", 2);
|
|
121
|
+
__decorateClass([
|
|
122
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "trust_state" })
|
|
123
|
+
], WagoController.prototype, "trustState", 2);
|
|
124
|
+
__decorateClass([
|
|
125
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", nullable: true })
|
|
126
|
+
], WagoController.prototype, "name", 2);
|
|
127
|
+
__decorateClass([
|
|
128
|
+
(0, import_plugins_backend_sdk.Column)({ type: "integer", name: "mqtt_server_id", nullable: true })
|
|
129
|
+
], WagoController.prototype, "mqttServerId", 2);
|
|
130
|
+
__decorateClass([
|
|
131
|
+
(0, import_plugins_backend_sdk.Column)({ type: "integer", name: "enrollment_id", nullable: true })
|
|
132
|
+
], WagoController.prototype, "enrollmentId", 2);
|
|
133
|
+
__decorateClass([
|
|
134
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "pairing_code_hash" })
|
|
135
|
+
], WagoController.prototype, "pairingCodeHash", 2);
|
|
136
|
+
__decorateClass([
|
|
137
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", nullable: true })
|
|
138
|
+
], WagoController.prototype, "fingerprint", 2);
|
|
139
|
+
__decorateClass([
|
|
140
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "protocol_version" })
|
|
141
|
+
], WagoController.prototype, "protocolVersion", 2);
|
|
142
|
+
__decorateClass([
|
|
143
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "runtime_version" })
|
|
144
|
+
], WagoController.prototype, "runtimeVersion", 2);
|
|
145
|
+
__decorateClass([
|
|
146
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar" })
|
|
147
|
+
], WagoController.prototype, "capabilities", 2);
|
|
148
|
+
__decorateClass([
|
|
149
|
+
(0, import_plugins_backend_sdk.Column)({ type: "integer", name: "last_sequence", default: 0 })
|
|
150
|
+
], WagoController.prototype, "lastSequence", 2);
|
|
151
|
+
__decorateClass([
|
|
152
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "last_heartbeat_at", nullable: true })
|
|
153
|
+
], WagoController.prototype, "lastHeartbeatAt", 2);
|
|
154
|
+
__decorateClass([
|
|
155
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "last_seen_at" })
|
|
156
|
+
], WagoController.prototype, "lastSeenAt", 2);
|
|
157
|
+
__decorateClass([
|
|
158
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "compatibility_error", nullable: true })
|
|
159
|
+
], WagoController.prototype, "compatibilityError", 2);
|
|
160
|
+
__decorateClass([
|
|
161
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "created_at" })
|
|
162
|
+
], WagoController.prototype, "createdAt", 2);
|
|
163
|
+
__decorateClass([
|
|
164
|
+
(0, import_plugins_backend_sdk.Column)({ type: "varchar", name: "updated_at" })
|
|
165
|
+
], WagoController.prototype, "updatedAt", 2);
|
|
166
|
+
WagoController = __decorateClass([
|
|
167
|
+
(0, import_plugins_backend_sdk.Entity)({ name: "plugin_wago_controllers" })
|
|
168
|
+
], WagoController);
|
|
169
|
+
|
|
170
|
+
// backend/wago-settings.entity.ts
|
|
171
|
+
var import_plugins_backend_sdk2 = require("@attraccess/plugins-backend-sdk");
|
|
172
|
+
var WagoSettings = class {
|
|
173
|
+
constructor() {
|
|
174
|
+
__publicField(this, "id");
|
|
175
|
+
__publicField(this, "defaultMqttServerId");
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
__decorateClass([
|
|
179
|
+
(0, import_plugins_backend_sdk2.PrimaryGeneratedColumn)({ type: "integer" })
|
|
180
|
+
], WagoSettings.prototype, "id", 2);
|
|
181
|
+
__decorateClass([
|
|
182
|
+
(0, import_plugins_backend_sdk2.Column)({ type: "integer", name: "default_mqtt_server_id", nullable: true })
|
|
183
|
+
], WagoSettings.prototype, "defaultMqttServerId", 2);
|
|
184
|
+
WagoSettings = __decorateClass([
|
|
185
|
+
(0, import_plugins_backend_sdk2.Entity)({ name: "plugin_wago_settings" })
|
|
186
|
+
], WagoSettings);
|
|
187
|
+
|
|
188
|
+
// backend/wago-enrollment.entity.ts
|
|
189
|
+
var import_plugins_backend_sdk3 = require("@attraccess/plugins-backend-sdk");
|
|
190
|
+
var WagoEnrollment = class {
|
|
191
|
+
constructor() {
|
|
192
|
+
__publicField(this, "id");
|
|
193
|
+
__publicField(this, "mqttServerId");
|
|
194
|
+
__publicField(this, "hardwareId");
|
|
195
|
+
__publicField(this, "secretHash");
|
|
196
|
+
__publicField(this, "identity");
|
|
197
|
+
__publicField(this, "createdAt");
|
|
198
|
+
__publicField(this, "expiresAt");
|
|
199
|
+
__publicField(this, "revokedAt");
|
|
200
|
+
__publicField(this, "consumedAt");
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
__decorateClass([
|
|
204
|
+
(0, import_plugins_backend_sdk3.PrimaryGeneratedColumn)({ type: "integer" })
|
|
205
|
+
], WagoEnrollment.prototype, "id", 2);
|
|
206
|
+
__decorateClass([
|
|
207
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "integer", name: "mqtt_server_id" })
|
|
208
|
+
], WagoEnrollment.prototype, "mqttServerId", 2);
|
|
209
|
+
__decorateClass([
|
|
210
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "hardware_id" })
|
|
211
|
+
], WagoEnrollment.prototype, "hardwareId", 2);
|
|
212
|
+
__decorateClass([
|
|
213
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "secret_hash", unique: true })
|
|
214
|
+
], WagoEnrollment.prototype, "secretHash", 2);
|
|
215
|
+
__decorateClass([
|
|
216
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar" })
|
|
217
|
+
], WagoEnrollment.prototype, "identity", 2);
|
|
218
|
+
__decorateClass([
|
|
219
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "created_at" })
|
|
220
|
+
], WagoEnrollment.prototype, "createdAt", 2);
|
|
221
|
+
__decorateClass([
|
|
222
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "expires_at" })
|
|
223
|
+
], WagoEnrollment.prototype, "expiresAt", 2);
|
|
224
|
+
__decorateClass([
|
|
225
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "revoked_at", nullable: true })
|
|
226
|
+
], WagoEnrollment.prototype, "revokedAt", 2);
|
|
227
|
+
__decorateClass([
|
|
228
|
+
(0, import_plugins_backend_sdk3.Column)({ type: "varchar", name: "consumed_at", nullable: true })
|
|
229
|
+
], WagoEnrollment.prototype, "consumedAt", 2);
|
|
230
|
+
WagoEnrollment = __decorateClass([
|
|
231
|
+
(0, import_plugins_backend_sdk3.Entity)({ name: "plugin_wago_enrollments" })
|
|
232
|
+
], WagoEnrollment);
|
|
233
|
+
|
|
234
|
+
// backend/wago.service.ts
|
|
235
|
+
var PLUGIN_CONTEXT = /* @__PURE__ */ Symbol.for("attraccess.plugin.context");
|
|
236
|
+
var STALE_AFTER_MS = 9e4;
|
|
237
|
+
var ENROLLMENT_RETRY_MS = 6e4;
|
|
238
|
+
var WagoService = class {
|
|
239
|
+
constructor(context) {
|
|
240
|
+
__publicField(this, "context", context);
|
|
241
|
+
__publicField(this, "controllers");
|
|
242
|
+
__publicField(this, "settings");
|
|
243
|
+
__publicField(this, "enrollments");
|
|
244
|
+
__publicField(this, "subscriptions", []);
|
|
245
|
+
__publicField(this, "enrollmentExpiryTimers", /* @__PURE__ */ new Map());
|
|
246
|
+
__publicField(this, "claimLocks", /* @__PURE__ */ new Map());
|
|
247
|
+
__publicField(this, "subscriptionRebuild", Promise.resolve());
|
|
248
|
+
__publicField(this, "subscriptionRetryTimer", null);
|
|
249
|
+
__publicField(this, "activeSubscriptionGeneration", 0);
|
|
250
|
+
__publicField(this, "destroyed", false);
|
|
251
|
+
this.controllers = context.getRepository(WagoController);
|
|
252
|
+
this.settings = context.getRepository(WagoSettings);
|
|
253
|
+
this.enrollments = context.getRepository(WagoEnrollment);
|
|
254
|
+
}
|
|
255
|
+
async onModuleInit() {
|
|
256
|
+
const enrollments = await this.enrollments.createQueryBuilder("enrollment").where("enrollment.consumedAt IS NULL").getMany();
|
|
257
|
+
for (const enrollment of enrollments) this.scheduleEnrollmentExpiry(enrollment);
|
|
258
|
+
await this.subscribeConfiguredServers();
|
|
259
|
+
}
|
|
260
|
+
onModuleDestroy() {
|
|
261
|
+
this.destroyed = true;
|
|
262
|
+
this.unsubscribe();
|
|
263
|
+
this.enrollmentExpiryTimers.forEach((timer) => clearTimeout(timer));
|
|
264
|
+
this.enrollmentExpiryTimers.clear();
|
|
265
|
+
if (this.subscriptionRetryTimer) clearTimeout(this.subscriptionRetryTimer);
|
|
266
|
+
}
|
|
267
|
+
async list() {
|
|
268
|
+
const controllers = await this.controllers.find({ order: { hardwareId: "ASC" } });
|
|
269
|
+
return controllers.map((controller) => ({
|
|
270
|
+
id: controller.id,
|
|
271
|
+
hardwareId: controller.hardwareId,
|
|
272
|
+
trustState: controller.trustState,
|
|
273
|
+
name: controller.name,
|
|
274
|
+
mqttServerId: controller.mqttServerId,
|
|
275
|
+
enrollmentId: controller.enrollmentId,
|
|
276
|
+
protocolVersion: controller.protocolVersion,
|
|
277
|
+
runtimeVersion: controller.runtimeVersion,
|
|
278
|
+
capabilities: controller.capabilities,
|
|
279
|
+
lastSequence: controller.lastSequence,
|
|
280
|
+
lastHeartbeatAt: controller.lastHeartbeatAt,
|
|
281
|
+
lastSeenAt: controller.lastSeenAt,
|
|
282
|
+
compatibilityError: controller.compatibilityError,
|
|
283
|
+
createdAt: controller.createdAt,
|
|
284
|
+
updatedAt: controller.updatedAt,
|
|
285
|
+
connectivity: this.connectivity(controller)
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
async getSettings() {
|
|
289
|
+
const settings = await this.settings.findOneBy({ id: 1 });
|
|
290
|
+
if (settings) return settings;
|
|
291
|
+
await this.settings.createQueryBuilder().insert().values({ id: 1, defaultMqttServerId: null }).orIgnore().execute();
|
|
292
|
+
return this.settings.findOneByOrFail({ id: 1 });
|
|
293
|
+
}
|
|
294
|
+
async setDefaultMqttServer(serverId) {
|
|
295
|
+
if (serverId !== null && !await this.context.getMqttServerConfig(serverId))
|
|
296
|
+
throw new import_common.NotFoundException(`MQTT server ${serverId} not found`);
|
|
297
|
+
const settings = await this.getSettings();
|
|
298
|
+
settings.defaultMqttServerId = serverId;
|
|
299
|
+
await this.settings.save(settings);
|
|
300
|
+
await this.subscribeConfiguredServers();
|
|
301
|
+
return settings;
|
|
302
|
+
}
|
|
303
|
+
async createEnrollment(hardwareId, mqttServerId, manualCredentials) {
|
|
304
|
+
const normalizedHardwareId = hardwareId.trim();
|
|
305
|
+
if (!isValidHardwareId(normalizedHardwareId))
|
|
306
|
+
throw new import_common.ConflictException("a valid hardware ID without MQTT separators or wildcards is required");
|
|
307
|
+
const selectedServerId = mqttServerId ?? (await this.getSettings()).defaultMqttServerId;
|
|
308
|
+
if (!selectedServerId) throw new import_common.ConflictException("select an MQTT server before creating an enrollment package");
|
|
309
|
+
const server = await this.context.getMqttServerConfig(selectedServerId);
|
|
310
|
+
if (!server) throw new import_common.NotFoundException(`MQTT server ${selectedServerId} not found`);
|
|
311
|
+
const claimSecret = (0, import_node_crypto.randomBytes)(24).toString("base64url");
|
|
312
|
+
const identity = `wago-enrollment-${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
|
|
313
|
+
const expiresAt = new Date(Date.now() + 15 * 6e4).toISOString();
|
|
314
|
+
const provisionedCredential = await this.context.getMqttCredentialProvisioning().provision({
|
|
315
|
+
mqttServerId: selectedServerId,
|
|
316
|
+
identity,
|
|
317
|
+
username: identity,
|
|
318
|
+
vhost: "/",
|
|
319
|
+
topicPolicy: {
|
|
320
|
+
publish: [discoveryTopic(normalizedHardwareId)],
|
|
321
|
+
subscribe: [`${discoveryTopic(normalizedHardwareId)}/claim`]
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
if (!("password" in provisionedCredential) && !manualCredentials)
|
|
325
|
+
throw new import_common.ConflictException(
|
|
326
|
+
`Manual discovery credentials are required: ${provisionedCredential.instructions.join(" ")}`
|
|
327
|
+
);
|
|
328
|
+
const credential = "password" in provisionedCredential ? provisionedCredential : manualCredentials;
|
|
329
|
+
if (!credential?.username.trim() || !credential.password)
|
|
330
|
+
throw new import_common.ConflictException("a manual discovery username and password are required");
|
|
331
|
+
const enrollment = await this.enrollments.save(
|
|
332
|
+
this.enrollments.create({
|
|
333
|
+
mqttServerId: selectedServerId,
|
|
334
|
+
hardwareId: normalizedHardwareId,
|
|
335
|
+
secretHash: hash(claimSecret),
|
|
336
|
+
identity: credential.username,
|
|
337
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
338
|
+
expiresAt
|
|
339
|
+
})
|
|
340
|
+
);
|
|
341
|
+
this.scheduleEnrollmentExpiry(enrollment);
|
|
342
|
+
await this.subscribeConfiguredServers().catch((error) => {
|
|
343
|
+
this.context.logger.warn(`Could not refresh WAGO MQTT subscriptions after enrollment: ${String(error)}`);
|
|
344
|
+
this.scheduleSubscriptionRetry();
|
|
345
|
+
});
|
|
346
|
+
return {
|
|
347
|
+
broker: { host: server.host, port: server.port, useTls: server.useTls },
|
|
348
|
+
username: credential.username,
|
|
349
|
+
password: "password" in credential ? credential.password : void 0,
|
|
350
|
+
claimSecret,
|
|
351
|
+
expiresAt,
|
|
352
|
+
manualInstructions: "instructions" in provisionedCredential ? provisionedCredential.instructions.map(
|
|
353
|
+
(instruction) => instruction.replaceAll(identity, () => credential.username)
|
|
354
|
+
) : void 0
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async claim(id, name, verifier, mqttServerId) {
|
|
358
|
+
return this.withClaimLock(id, () => this.claimController(id, name, verifier, mqttServerId));
|
|
359
|
+
}
|
|
360
|
+
async claimController(id, name, verifier, mqttServerId) {
|
|
361
|
+
const controller = await this.controllers.findOneBy({ id });
|
|
362
|
+
if (!controller) throw new import_common.NotFoundException(`WAGO controller ${id} not found`);
|
|
363
|
+
if (controller.trustState === "claimed") throw new import_common.ConflictException("controller has already been claimed");
|
|
364
|
+
if (!name.trim()) throw new import_common.ConflictException("a controller name is required");
|
|
365
|
+
if (!this.matchesVerifier(controller, verifier))
|
|
366
|
+
throw new import_common.ConflictException("physical pairing code or fingerprint does not match the controller");
|
|
367
|
+
if (controller.compatibilityError) throw new import_common.ConflictException(controller.compatibilityError);
|
|
368
|
+
const selectedServerId = mqttServerId ?? controller.mqttServerId;
|
|
369
|
+
if (!selectedServerId) throw new import_common.ConflictException("select an MQTT server before claiming this controller");
|
|
370
|
+
if (selectedServerId !== controller.mqttServerId)
|
|
371
|
+
throw new import_common.ConflictException("claim the controller on the MQTT server used for its enrollment package");
|
|
372
|
+
if (!await this.context.getMqttServerConfig(selectedServerId))
|
|
373
|
+
throw new import_common.NotFoundException(`MQTT server ${selectedServerId} not found`);
|
|
374
|
+
const enrollment = await this.activeEnrollment(controller.enrollmentId);
|
|
375
|
+
if (!enrollment)
|
|
376
|
+
throw new import_common.ConflictException(
|
|
377
|
+
"the controller enrollment package has expired or was already consumed; create a new one"
|
|
378
|
+
);
|
|
379
|
+
const identity = `wago-controller-${controller.hardwareId}`;
|
|
380
|
+
const credential = await this.context.getMqttCredentialProvisioning().provision({
|
|
381
|
+
mqttServerId: selectedServerId,
|
|
382
|
+
identity,
|
|
383
|
+
username: identity,
|
|
384
|
+
vhost: "/",
|
|
385
|
+
topicPolicy: {
|
|
386
|
+
publish: [`attraccess/wago/controllers/${controller.hardwareId}/#`],
|
|
387
|
+
subscribe: [`attraccess/wago/controllers/${controller.hardwareId}/desired/#`]
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
if (!("password" in credential)) {
|
|
391
|
+
throw new import_common.ConflictException(`Manual credential provisioning is required: ${credential.instructions.join(" ")}`);
|
|
392
|
+
}
|
|
393
|
+
let credentialDelivered = false;
|
|
394
|
+
const previousController = {
|
|
395
|
+
trustState: controller.trustState,
|
|
396
|
+
name: controller.name,
|
|
397
|
+
mqttServerId: controller.mqttServerId,
|
|
398
|
+
updatedAt: controller.updatedAt
|
|
399
|
+
};
|
|
400
|
+
try {
|
|
401
|
+
await this.revokeEnrollment(enrollment);
|
|
402
|
+
controller.trustState = "claimed";
|
|
403
|
+
controller.name = name.trim();
|
|
404
|
+
controller.mqttServerId = selectedServerId;
|
|
405
|
+
controller.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
406
|
+
await this.controllers.save(controller);
|
|
407
|
+
await this.context.mqtt.publish(
|
|
408
|
+
selectedServerId,
|
|
409
|
+
`${discoveryTopic(controller.hardwareId)}/claim`,
|
|
410
|
+
JSON.stringify({ username: credential.username, password: credential.password }),
|
|
411
|
+
{ qos: 1 }
|
|
412
|
+
);
|
|
413
|
+
credentialDelivered = true;
|
|
414
|
+
await this.context.mqtt.publish(selectedServerId, discoveryTopic(controller.hardwareId), "", {
|
|
415
|
+
qos: 1,
|
|
416
|
+
retain: true
|
|
417
|
+
});
|
|
418
|
+
return controller;
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (!credentialDelivered) {
|
|
421
|
+
await this.context.getMqttCredentialProvisioning().revoke({ mqttServerId: selectedServerId, identity, username: identity, vhost: "/" }).catch(() => void 0);
|
|
422
|
+
Object.assign(controller, previousController);
|
|
423
|
+
await this.controllers.save(controller).catch((rollbackError) => {
|
|
424
|
+
this.context.logger.warn(
|
|
425
|
+
`Could not restore WAGO controller ${controller.id} after claim failure: ${String(rollbackError)}`
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
throw error;
|
|
430
|
+
} finally {
|
|
431
|
+
await this.subscribeConfiguredServers().catch((error) => {
|
|
432
|
+
this.context.logger.warn(`Could not refresh WAGO MQTT subscriptions after claim: ${String(error)}`);
|
|
433
|
+
this.scheduleSubscriptionRetry();
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async subscribeConfiguredServers() {
|
|
438
|
+
if (this.destroyed) return;
|
|
439
|
+
const rebuild = this.subscriptionRebuild.then(() => this.rebuildSubscriptions());
|
|
440
|
+
this.subscriptionRebuild = rebuild.catch(() => void 0);
|
|
441
|
+
return rebuild;
|
|
442
|
+
}
|
|
443
|
+
async rebuildSubscriptions() {
|
|
444
|
+
if (this.destroyed) return;
|
|
445
|
+
const settings = await this.getSettings();
|
|
446
|
+
const [controllers, enrollments] = await Promise.all([this.controllers.find(), this.activeEnrollments()]);
|
|
447
|
+
const serverIds = /* @__PURE__ */ new Set();
|
|
448
|
+
if (settings.defaultMqttServerId) serverIds.add(settings.defaultMqttServerId);
|
|
449
|
+
controllers.filter((controller) => controller.trustState === "claimed").forEach((controller) => {
|
|
450
|
+
const serverId = controller.mqttServerId ?? settings.defaultMqttServerId;
|
|
451
|
+
if (serverId) serverIds.add(serverId);
|
|
452
|
+
});
|
|
453
|
+
enrollments.forEach((enrollment) => {
|
|
454
|
+
serverIds.add(enrollment.mqttServerId);
|
|
455
|
+
});
|
|
456
|
+
const generation = this.activeSubscriptionGeneration + 1;
|
|
457
|
+
const replacements = [];
|
|
458
|
+
try {
|
|
459
|
+
for (const serverId of serverIds) {
|
|
460
|
+
replacements.push(
|
|
461
|
+
await this.context.mqtt.subscribe(serverId, `${DISCOVERY_ROOT}/+`, async (message) => {
|
|
462
|
+
if (!this.isActiveSubscriptionGeneration(generation)) return;
|
|
463
|
+
await this.onDiscovery(serverId, message.topic, message.payload);
|
|
464
|
+
})
|
|
465
|
+
);
|
|
466
|
+
for (const controller of controllers.filter(
|
|
467
|
+
(item) => item.trustState === "claimed" && (item.mqttServerId ?? settings.defaultMqttServerId) === serverId
|
|
468
|
+
)) {
|
|
469
|
+
replacements.push(
|
|
470
|
+
await this.context.mqtt.subscribe(serverId, heartbeatTopic(controller.hardwareId), async (message) => {
|
|
471
|
+
if (!this.isActiveSubscriptionGeneration(generation)) return;
|
|
472
|
+
await this.onHeartbeat(controller.hardwareId, message.payload);
|
|
473
|
+
})
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} catch (error) {
|
|
478
|
+
replacements.forEach((subscription) => subscription.unsubscribe());
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
if (this.destroyed) {
|
|
482
|
+
replacements.forEach((subscription) => subscription.unsubscribe());
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
this.activeSubscriptionGeneration = generation;
|
|
486
|
+
this.unsubscribe();
|
|
487
|
+
this.subscriptions.push(...replacements);
|
|
488
|
+
}
|
|
489
|
+
unsubscribe() {
|
|
490
|
+
this.subscriptions.splice(0).forEach((subscription) => subscription.unsubscribe());
|
|
491
|
+
}
|
|
492
|
+
isActiveSubscriptionGeneration(generation) {
|
|
493
|
+
return !this.destroyed && generation === this.activeSubscriptionGeneration;
|
|
494
|
+
}
|
|
495
|
+
async onDiscovery(serverId, topic, payload) {
|
|
496
|
+
const hardwareId = topic.slice(`${DISCOVERY_ROOT}/`.length);
|
|
497
|
+
if (!isValidHardwareId(hardwareId)) return;
|
|
498
|
+
let announcement;
|
|
499
|
+
try {
|
|
500
|
+
announcement = parseAnnouncement(payload);
|
|
501
|
+
} catch (error) {
|
|
502
|
+
this.context.logger.warn(
|
|
503
|
+
`Ignoring invalid WAGO announcement: ${error instanceof Error ? error.message : String(error)}`
|
|
504
|
+
);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (announcement.hardwareId !== hardwareId) {
|
|
508
|
+
this.context.logger.warn(`Ignoring WAGO announcement with mismatched hardware ID on ${topic}`);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const enrollment = announcement.enrollmentSecret ? await this.validEnrollment(announcement.enrollmentSecret, serverId, hardwareId) : null;
|
|
512
|
+
if (!enrollment) {
|
|
513
|
+
this.context.logger.warn(`Ignoring WAGO announcement without a valid enrollment secret on ${topic}`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const existing = await this.controllers.findOneBy({ hardwareId });
|
|
517
|
+
if (existing?.trustState === "claimed") return;
|
|
518
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
519
|
+
const candidate = existing ?? this.controllers.create({
|
|
520
|
+
hardwareId,
|
|
521
|
+
trustState: "untrusted",
|
|
522
|
+
name: null,
|
|
523
|
+
mqttServerId: serverId,
|
|
524
|
+
enrollmentId: enrollment.id,
|
|
525
|
+
pairingCodeHash: "",
|
|
526
|
+
fingerprint: null,
|
|
527
|
+
protocolVersion: "",
|
|
528
|
+
runtimeVersion: "",
|
|
529
|
+
capabilities: "[]",
|
|
530
|
+
lastSequence: 0,
|
|
531
|
+
lastHeartbeatAt: null,
|
|
532
|
+
lastSeenAt: now,
|
|
533
|
+
compatibilityError: null,
|
|
534
|
+
createdAt: now,
|
|
535
|
+
updatedAt: now
|
|
536
|
+
});
|
|
537
|
+
candidate.mqttServerId = serverId;
|
|
538
|
+
candidate.enrollmentId = enrollment.id;
|
|
539
|
+
candidate.pairingCodeHash = hash(announcement.pairingCode);
|
|
540
|
+
candidate.fingerprint = announcement.fingerprint ?? null;
|
|
541
|
+
candidate.protocolVersion = announcement.protocolVersion;
|
|
542
|
+
candidate.runtimeVersion = announcement.runtimeVersion;
|
|
543
|
+
candidate.capabilities = JSON.stringify(announcement.capabilities);
|
|
544
|
+
candidate.lastSequence = announcement.sequence ?? candidate.lastSequence;
|
|
545
|
+
candidate.lastSeenAt = now;
|
|
546
|
+
candidate.compatibilityError = compatibilityError(announcement);
|
|
547
|
+
candidate.updatedAt = now;
|
|
548
|
+
await this.controllers.save(candidate);
|
|
549
|
+
}
|
|
550
|
+
async onHeartbeat(hardwareId, payload) {
|
|
551
|
+
let heartbeat;
|
|
552
|
+
try {
|
|
553
|
+
heartbeat = parseAnnouncement(payload);
|
|
554
|
+
} catch (error) {
|
|
555
|
+
this.context.logger.warn(
|
|
556
|
+
`Ignoring invalid WAGO heartbeat: ${error instanceof Error ? error.message : String(error)}`
|
|
557
|
+
);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (heartbeat.hardwareId !== hardwareId) return;
|
|
561
|
+
const controller = await this.controllers.findOneBy({ hardwareId });
|
|
562
|
+
if (!controller || controller.trustState !== "claimed" || heartbeat.sequence !== void 0 && heartbeat.sequence < controller.lastSequence)
|
|
563
|
+
return;
|
|
564
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
565
|
+
controller.protocolVersion = heartbeat.protocolVersion;
|
|
566
|
+
controller.runtimeVersion = heartbeat.runtimeVersion;
|
|
567
|
+
controller.capabilities = JSON.stringify(heartbeat.capabilities);
|
|
568
|
+
controller.lastSequence = heartbeat.sequence ?? controller.lastSequence;
|
|
569
|
+
controller.lastHeartbeatAt = now;
|
|
570
|
+
controller.lastSeenAt = now;
|
|
571
|
+
controller.compatibilityError = compatibilityError(heartbeat);
|
|
572
|
+
controller.updatedAt = now;
|
|
573
|
+
await this.controllers.save(controller);
|
|
574
|
+
}
|
|
575
|
+
connectivity(controller) {
|
|
576
|
+
if (controller.trustState === "untrusted") return "untrusted";
|
|
577
|
+
return controller.lastHeartbeatAt && Date.now() - Date.parse(controller.lastHeartbeatAt) <= STALE_AFTER_MS ? "online" : "stale";
|
|
578
|
+
}
|
|
579
|
+
matchesVerifier(controller, verifier) {
|
|
580
|
+
const value = verifier.trim();
|
|
581
|
+
return Boolean(value) && Boolean(controller.fingerprint) && value === controller.fingerprint || safeEqual(hash(value), controller.pairingCodeHash);
|
|
582
|
+
}
|
|
583
|
+
async validEnrollment(secret, serverId, hardwareId) {
|
|
584
|
+
const enrollment = await this.enrollments.findOneBy({
|
|
585
|
+
secretHash: hash(secret),
|
|
586
|
+
mqttServerId: serverId,
|
|
587
|
+
hardwareId
|
|
588
|
+
});
|
|
589
|
+
return enrollment && this.isActiveEnrollment(enrollment) ? enrollment : null;
|
|
590
|
+
}
|
|
591
|
+
async activeEnrollment(id) {
|
|
592
|
+
if (!id) return null;
|
|
593
|
+
const enrollment = await this.enrollments.findOneBy({ id });
|
|
594
|
+
return enrollment && this.isActiveEnrollment(enrollment) ? enrollment : null;
|
|
595
|
+
}
|
|
596
|
+
activeEnrollments() {
|
|
597
|
+
return this.enrollments.createQueryBuilder("enrollment").where("enrollment.consumedAt IS NULL").andWhere("enrollment.revokedAt IS NULL").andWhere("enrollment.expiresAt > :now", { now: (/* @__PURE__ */ new Date()).toISOString() }).getMany();
|
|
598
|
+
}
|
|
599
|
+
isActiveEnrollment(enrollment) {
|
|
600
|
+
return !enrollment.consumedAt && !enrollment.revokedAt && Date.parse(enrollment.expiresAt) > Date.now();
|
|
601
|
+
}
|
|
602
|
+
scheduleEnrollmentExpiry(enrollment, delay = Date.parse(enrollment.expiresAt) - Date.now()) {
|
|
603
|
+
if (enrollment.consumedAt) return;
|
|
604
|
+
const existing = this.enrollmentExpiryTimers.get(enrollment.id);
|
|
605
|
+
if (existing) clearTimeout(existing);
|
|
606
|
+
this.enrollmentExpiryTimers.set(
|
|
607
|
+
enrollment.id,
|
|
608
|
+
setTimeout(
|
|
609
|
+
() => {
|
|
610
|
+
this.enrollmentExpiryTimers.delete(enrollment.id);
|
|
611
|
+
if (this.destroyed) return;
|
|
612
|
+
void this.revokeEnrollment(enrollment).then(() => this.subscribeConfiguredServers()).catch((error) => {
|
|
613
|
+
this.context.logger.warn(`Could not revoke expired WAGO enrollment ${enrollment.id}: ${String(error)}`);
|
|
614
|
+
if (!enrollment.consumedAt) this.scheduleEnrollmentExpiry(enrollment, ENROLLMENT_RETRY_MS);
|
|
615
|
+
this.scheduleSubscriptionRetry();
|
|
616
|
+
});
|
|
617
|
+
},
|
|
618
|
+
Math.max(0, delay)
|
|
619
|
+
)
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
async revokeEnrollment(enrollment) {
|
|
623
|
+
if (!enrollment.revokedAt) {
|
|
624
|
+
const manual = await this.context.getMqttCredentialProvisioning().revoke({
|
|
625
|
+
mqttServerId: enrollment.mqttServerId,
|
|
626
|
+
identity: enrollment.identity,
|
|
627
|
+
username: enrollment.identity,
|
|
628
|
+
vhost: "/"
|
|
629
|
+
});
|
|
630
|
+
if (manual)
|
|
631
|
+
throw new import_common.ConflictException(`Manual credential revocation is required: ${manual.instructions.join(" ")}`);
|
|
632
|
+
enrollment.revokedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
633
|
+
await this.enrollments.save(enrollment);
|
|
634
|
+
}
|
|
635
|
+
enrollment.consumedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
636
|
+
await this.enrollments.save(enrollment);
|
|
637
|
+
const timer = this.enrollmentExpiryTimers.get(enrollment.id);
|
|
638
|
+
if (timer) clearTimeout(timer);
|
|
639
|
+
this.enrollmentExpiryTimers.delete(enrollment.id);
|
|
640
|
+
}
|
|
641
|
+
async withClaimLock(id, operation) {
|
|
642
|
+
const previous = this.claimLocks.get(id) ?? Promise.resolve();
|
|
643
|
+
let release;
|
|
644
|
+
const lock = new Promise((resolve) => {
|
|
645
|
+
release = resolve;
|
|
646
|
+
});
|
|
647
|
+
this.claimLocks.set(id, lock);
|
|
648
|
+
await previous;
|
|
649
|
+
try {
|
|
650
|
+
return await operation();
|
|
651
|
+
} finally {
|
|
652
|
+
release();
|
|
653
|
+
if (this.claimLocks.get(id) === lock) this.claimLocks.delete(id);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
scheduleSubscriptionRetry() {
|
|
657
|
+
if (this.destroyed || this.subscriptionRetryTimer) return;
|
|
658
|
+
this.subscriptionRetryTimer = setTimeout(() => {
|
|
659
|
+
this.subscriptionRetryTimer = null;
|
|
660
|
+
if (this.destroyed) return;
|
|
661
|
+
void this.subscribeConfiguredServers().catch((error) => {
|
|
662
|
+
this.context.logger.warn(`Could not refresh WAGO MQTT subscriptions: ${String(error)}`);
|
|
663
|
+
this.scheduleSubscriptionRetry();
|
|
664
|
+
});
|
|
665
|
+
}, ENROLLMENT_RETRY_MS);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
WagoService = __decorateClass([
|
|
669
|
+
(0, import_common.Injectable)(),
|
|
670
|
+
__decorateParam(0, (0, import_common.Inject)(PLUGIN_CONTEXT))
|
|
671
|
+
], WagoService);
|
|
672
|
+
function hash(value) {
|
|
673
|
+
return (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
674
|
+
}
|
|
675
|
+
function safeEqual(left, right) {
|
|
676
|
+
return left.length === right.length && (0, import_node_crypto.timingSafeEqual)(Buffer.from(left), Buffer.from(right));
|
|
677
|
+
}
|
|
678
|
+
function isValidHardwareId(hardwareId) {
|
|
679
|
+
return Boolean(hardwareId) && !/[/+#]/.test(hardwareId);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// backend/wago.controller.ts
|
|
683
|
+
var WagoControllerApi = class {
|
|
684
|
+
constructor(wago) {
|
|
685
|
+
__publicField(this, "wago", wago);
|
|
686
|
+
}
|
|
687
|
+
list() {
|
|
688
|
+
return this.wago.list();
|
|
689
|
+
}
|
|
690
|
+
settings() {
|
|
691
|
+
return this.wago.getSettings();
|
|
692
|
+
}
|
|
693
|
+
setSettings(body) {
|
|
694
|
+
return this.wago.setDefaultMqttServer(body?.defaultMqttServerId ?? null);
|
|
695
|
+
}
|
|
696
|
+
enrollment(body) {
|
|
697
|
+
const manualCredentials = body?.manualUsername || body?.manualPassword ? { username: body.manualUsername ?? "", password: body.manualPassword ?? "" } : void 0;
|
|
698
|
+
return this.wago.createEnrollment(body?.hardwareId ?? "", body?.mqttServerId, manualCredentials);
|
|
699
|
+
}
|
|
700
|
+
claim(id, body) {
|
|
701
|
+
return this.wago.claim(id, body?.name ?? "", body?.verifier ?? "", body?.mqttServerId);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
__decorateClass([
|
|
705
|
+
(0, import_common2.Get)("controllers")
|
|
706
|
+
], WagoControllerApi.prototype, "list", 1);
|
|
707
|
+
__decorateClass([
|
|
708
|
+
(0, import_common2.Get)("settings")
|
|
709
|
+
], WagoControllerApi.prototype, "settings", 1);
|
|
710
|
+
__decorateClass([
|
|
711
|
+
(0, import_common2.Post)("settings"),
|
|
712
|
+
__decorateParam(0, (0, import_common2.Body)())
|
|
713
|
+
], WagoControllerApi.prototype, "setSettings", 1);
|
|
714
|
+
__decorateClass([
|
|
715
|
+
(0, import_common2.Post)("enrollments"),
|
|
716
|
+
__decorateParam(0, (0, import_common2.Body)())
|
|
717
|
+
], WagoControllerApi.prototype, "enrollment", 1);
|
|
718
|
+
__decorateClass([
|
|
719
|
+
(0, import_common2.Post)("controllers/:id/claim"),
|
|
720
|
+
__decorateParam(0, (0, import_common2.Param)("id", import_common2.ParseIntPipe)),
|
|
721
|
+
__decorateParam(1, (0, import_common2.Body)())
|
|
722
|
+
], WagoControllerApi.prototype, "claim", 1);
|
|
723
|
+
WagoControllerApi = __decorateClass([
|
|
724
|
+
(0, import_plugins_backend_sdk4.Auth)("resources.update"),
|
|
725
|
+
(0, import_common2.Controller)("wago"),
|
|
726
|
+
__decorateParam(0, (0, import_common2.Inject)(WagoService))
|
|
727
|
+
], WagoControllerApi);
|
|
728
|
+
|
|
729
|
+
// backend/plugin.ts
|
|
730
|
+
var PLUGIN_CONTEXT2 = /* @__PURE__ */ Symbol.for("attraccess.plugin.context");
|
|
731
|
+
var WagoPluginModule = class {
|
|
732
|
+
};
|
|
733
|
+
var plugin = {
|
|
734
|
+
entities: [WagoController, WagoSettings, WagoEnrollment],
|
|
735
|
+
register(context) {
|
|
736
|
+
return {
|
|
737
|
+
module: WagoPluginModule,
|
|
738
|
+
controllers: [WagoControllerApi],
|
|
739
|
+
providers: [{ provide: PLUGIN_CONTEXT2, useValue: context }, WagoService]
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
var plugin_default = plugin;
|