@openmdm/hono 0.2.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/dist/index.d.ts +67 -0
- package/dist/index.js +462 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/index.ts +746 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present OpenMDM Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Context, Hono } from 'hono';
|
|
2
|
+
import { MDMInstance } from '@openmdm/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* OpenMDM Hono Adapter
|
|
6
|
+
*
|
|
7
|
+
* HTTP routes adapter for Hono framework.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Hono } from 'hono';
|
|
12
|
+
* import { createMDM } from '@openmdm/core';
|
|
13
|
+
* import { honoAdapter } from '@openmdm/hono';
|
|
14
|
+
*
|
|
15
|
+
* const mdm = createMDM({ ... });
|
|
16
|
+
* const app = new Hono<MDMEnv>();
|
|
17
|
+
*
|
|
18
|
+
* // Mount MDM routes
|
|
19
|
+
* app.route('/mdm', honoAdapter(mdm));
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Context variables set by OpenMDM middlewares
|
|
25
|
+
*/
|
|
26
|
+
interface MDMVariables {
|
|
27
|
+
deviceId?: string;
|
|
28
|
+
user?: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Hono environment type for OpenMDM routes
|
|
32
|
+
*/
|
|
33
|
+
type MDMEnv = {
|
|
34
|
+
Variables: MDMVariables;
|
|
35
|
+
};
|
|
36
|
+
interface HonoAdapterOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Base path prefix for all routes (default: '')
|
|
39
|
+
*/
|
|
40
|
+
basePath?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Enable authentication middleware for admin routes
|
|
43
|
+
*/
|
|
44
|
+
enableAuth?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Custom error handler
|
|
47
|
+
*/
|
|
48
|
+
onError?: (error: Error, c: Context) => Response | Promise<Response>;
|
|
49
|
+
/**
|
|
50
|
+
* Routes to expose (default: all)
|
|
51
|
+
*/
|
|
52
|
+
routes?: {
|
|
53
|
+
enrollment?: boolean;
|
|
54
|
+
devices?: boolean;
|
|
55
|
+
policies?: boolean;
|
|
56
|
+
applications?: boolean;
|
|
57
|
+
groups?: boolean;
|
|
58
|
+
commands?: boolean;
|
|
59
|
+
events?: boolean;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create a Hono router with OpenMDM API routes
|
|
64
|
+
*/
|
|
65
|
+
declare function honoAdapter(mdm: MDMInstance, options?: HonoAdapterOptions): Hono<MDMEnv>;
|
|
66
|
+
|
|
67
|
+
export { type HonoAdapterOptions, honoAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { HTTPException } from "hono/http-exception";
|
|
4
|
+
function honoAdapter(mdm, options = {}) {
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
const routes = {
|
|
7
|
+
enrollment: true,
|
|
8
|
+
devices: true,
|
|
9
|
+
policies: true,
|
|
10
|
+
applications: true,
|
|
11
|
+
groups: true,
|
|
12
|
+
commands: true,
|
|
13
|
+
events: true,
|
|
14
|
+
...options.routes
|
|
15
|
+
};
|
|
16
|
+
app.onError((error, c) => {
|
|
17
|
+
if (options.onError) {
|
|
18
|
+
return options.onError(error, c);
|
|
19
|
+
}
|
|
20
|
+
console.error("[OpenMDM] Error:", error);
|
|
21
|
+
if (error instanceof HTTPException) {
|
|
22
|
+
return c.json({ error: error.message }, error.status);
|
|
23
|
+
}
|
|
24
|
+
const mdmError = error;
|
|
25
|
+
if (mdmError.code && mdmError.statusCode) {
|
|
26
|
+
return c.json(
|
|
27
|
+
{
|
|
28
|
+
error: mdmError.message,
|
|
29
|
+
code: mdmError.code,
|
|
30
|
+
details: mdmError.details
|
|
31
|
+
},
|
|
32
|
+
mdmError.statusCode
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
36
|
+
});
|
|
37
|
+
const deviceAuth = async (c, next) => {
|
|
38
|
+
const token = c.req.header("Authorization")?.replace("Bearer ", "");
|
|
39
|
+
const deviceId = c.req.header("X-Device-Id");
|
|
40
|
+
if (!token && !deviceId) {
|
|
41
|
+
throw new HTTPException(401, { message: "Device authentication required" });
|
|
42
|
+
}
|
|
43
|
+
if (token) {
|
|
44
|
+
const result = await mdm.verifyDeviceToken(token);
|
|
45
|
+
if (!result) {
|
|
46
|
+
throw new HTTPException(401, { message: "Invalid device token" });
|
|
47
|
+
}
|
|
48
|
+
c.set("deviceId", result.deviceId);
|
|
49
|
+
} else if (deviceId) {
|
|
50
|
+
c.set("deviceId", deviceId);
|
|
51
|
+
}
|
|
52
|
+
await next();
|
|
53
|
+
};
|
|
54
|
+
const adminAuth = async (c, next) => {
|
|
55
|
+
if (!mdm.config.auth) {
|
|
56
|
+
await next();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const user = await mdm.config.auth.getUser(c);
|
|
60
|
+
if (!user) {
|
|
61
|
+
throw new HTTPException(401, { message: "Authentication required" });
|
|
62
|
+
}
|
|
63
|
+
if (mdm.config.auth.isAdmin) {
|
|
64
|
+
const isAdmin = await mdm.config.auth.isAdmin(user);
|
|
65
|
+
if (!isAdmin) {
|
|
66
|
+
throw new HTTPException(403, { message: "Admin access required" });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
c.set("user", user);
|
|
70
|
+
await next();
|
|
71
|
+
};
|
|
72
|
+
if (routes.enrollment) {
|
|
73
|
+
const enrollment = new Hono();
|
|
74
|
+
enrollment.post("/enroll", async (c) => {
|
|
75
|
+
const body = await c.req.json();
|
|
76
|
+
if (!body.model || !body.manufacturer || !body.osVersion) {
|
|
77
|
+
throw new HTTPException(400, {
|
|
78
|
+
message: "Missing required fields: model, manufacturer, osVersion"
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!body.macAddress && !body.serialNumber && !body.imei && !body.androidId) {
|
|
82
|
+
throw new HTTPException(400, {
|
|
83
|
+
message: "At least one device identifier required"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const result = await mdm.enroll(body);
|
|
87
|
+
if (!result.serverUrl) {
|
|
88
|
+
const url = new URL(c.req.url);
|
|
89
|
+
result.serverUrl = `${url.protocol}//${url.host}`;
|
|
90
|
+
}
|
|
91
|
+
return c.json(result, 201);
|
|
92
|
+
});
|
|
93
|
+
enrollment.post("/heartbeat", deviceAuth, async (c) => {
|
|
94
|
+
const deviceId = c.get("deviceId");
|
|
95
|
+
const body = await c.req.json();
|
|
96
|
+
await mdm.processHeartbeat(deviceId, {
|
|
97
|
+
...body,
|
|
98
|
+
deviceId,
|
|
99
|
+
timestamp: new Date(body.timestamp || Date.now())
|
|
100
|
+
});
|
|
101
|
+
const pendingCommands = await mdm.commands.getPending(deviceId);
|
|
102
|
+
return c.json({
|
|
103
|
+
status: "ok",
|
|
104
|
+
commands: pendingCommands
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
enrollment.get("/config", deviceAuth, async (c) => {
|
|
108
|
+
const deviceId = c.get("deviceId");
|
|
109
|
+
const device = await mdm.devices.get(deviceId);
|
|
110
|
+
if (!device) {
|
|
111
|
+
throw new HTTPException(404, { message: "Device not found" });
|
|
112
|
+
}
|
|
113
|
+
let policy = null;
|
|
114
|
+
if (device.policyId) {
|
|
115
|
+
policy = await mdm.policies.get(device.policyId);
|
|
116
|
+
} else {
|
|
117
|
+
policy = await mdm.policies.getDefault();
|
|
118
|
+
}
|
|
119
|
+
return c.json({
|
|
120
|
+
device: {
|
|
121
|
+
id: device.id,
|
|
122
|
+
enrollmentId: device.enrollmentId,
|
|
123
|
+
status: device.status
|
|
124
|
+
},
|
|
125
|
+
policy
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
enrollment.post("/push-token", deviceAuth, async (c) => {
|
|
129
|
+
const deviceId = c.get("deviceId");
|
|
130
|
+
const body = await c.req.json();
|
|
131
|
+
await mdm.db.upsertPushToken({
|
|
132
|
+
deviceId,
|
|
133
|
+
provider: body.provider,
|
|
134
|
+
token: body.token
|
|
135
|
+
});
|
|
136
|
+
return c.json({ status: "ok" });
|
|
137
|
+
});
|
|
138
|
+
enrollment.post("/commands/:id/ack", deviceAuth, async (c) => {
|
|
139
|
+
const commandId = c.req.param("id");
|
|
140
|
+
const command = await mdm.commands.acknowledge(commandId);
|
|
141
|
+
return c.json(command);
|
|
142
|
+
});
|
|
143
|
+
enrollment.post("/commands/:id/complete", deviceAuth, async (c) => {
|
|
144
|
+
const commandId = c.req.param("id");
|
|
145
|
+
const body = await c.req.json();
|
|
146
|
+
const command = await mdm.commands.complete(commandId, body);
|
|
147
|
+
return c.json(command);
|
|
148
|
+
});
|
|
149
|
+
enrollment.post("/commands/:id/fail", deviceAuth, async (c) => {
|
|
150
|
+
const commandId = c.req.param("id");
|
|
151
|
+
const body = await c.req.json();
|
|
152
|
+
const command = await mdm.commands.fail(commandId, body.error);
|
|
153
|
+
return c.json(command);
|
|
154
|
+
});
|
|
155
|
+
app.route("/agent", enrollment);
|
|
156
|
+
}
|
|
157
|
+
if (routes.devices) {
|
|
158
|
+
const devices = new Hono();
|
|
159
|
+
if (options.enableAuth) {
|
|
160
|
+
devices.use("/*", adminAuth);
|
|
161
|
+
}
|
|
162
|
+
devices.get("/", async (c) => {
|
|
163
|
+
const filter = {
|
|
164
|
+
status: c.req.query("status"),
|
|
165
|
+
policyId: c.req.query("policyId"),
|
|
166
|
+
groupId: c.req.query("groupId"),
|
|
167
|
+
search: c.req.query("search"),
|
|
168
|
+
limit: c.req.query("limit") ? parseInt(c.req.query("limit")) : void 0,
|
|
169
|
+
offset: c.req.query("offset") ? parseInt(c.req.query("offset")) : void 0
|
|
170
|
+
};
|
|
171
|
+
const result = await mdm.devices.list(filter);
|
|
172
|
+
return c.json(result);
|
|
173
|
+
});
|
|
174
|
+
devices.get("/:id", async (c) => {
|
|
175
|
+
const device = await mdm.devices.get(c.req.param("id"));
|
|
176
|
+
if (!device) {
|
|
177
|
+
throw new HTTPException(404, { message: "Device not found" });
|
|
178
|
+
}
|
|
179
|
+
return c.json(device);
|
|
180
|
+
});
|
|
181
|
+
devices.patch("/:id", async (c) => {
|
|
182
|
+
const body = await c.req.json();
|
|
183
|
+
const device = await mdm.devices.update(c.req.param("id"), body);
|
|
184
|
+
return c.json(device);
|
|
185
|
+
});
|
|
186
|
+
devices.delete("/:id", async (c) => {
|
|
187
|
+
await mdm.devices.delete(c.req.param("id"));
|
|
188
|
+
return c.json({ status: "ok" });
|
|
189
|
+
});
|
|
190
|
+
devices.post("/:id/policy", async (c) => {
|
|
191
|
+
const { policyId } = await c.req.json();
|
|
192
|
+
const device = await mdm.devices.assignPolicy(c.req.param("id"), policyId);
|
|
193
|
+
return c.json(device);
|
|
194
|
+
});
|
|
195
|
+
devices.get("/:id/groups", async (c) => {
|
|
196
|
+
const groups = await mdm.devices.getGroups(c.req.param("id"));
|
|
197
|
+
return c.json({ groups });
|
|
198
|
+
});
|
|
199
|
+
devices.post("/:id/groups", async (c) => {
|
|
200
|
+
const { groupId } = await c.req.json();
|
|
201
|
+
await mdm.devices.addToGroup(c.req.param("id"), groupId);
|
|
202
|
+
return c.json({ status: "ok" });
|
|
203
|
+
});
|
|
204
|
+
devices.delete("/:id/groups/:groupId", async (c) => {
|
|
205
|
+
await mdm.devices.removeFromGroup(c.req.param("id"), c.req.param("groupId"));
|
|
206
|
+
return c.json({ status: "ok" });
|
|
207
|
+
});
|
|
208
|
+
devices.post("/:id/commands", async (c) => {
|
|
209
|
+
const body = await c.req.json();
|
|
210
|
+
const command = await mdm.devices.sendCommand(c.req.param("id"), body);
|
|
211
|
+
return c.json(command, 201);
|
|
212
|
+
});
|
|
213
|
+
devices.post("/:id/sync", async (c) => {
|
|
214
|
+
const command = await mdm.devices.sync(c.req.param("id"));
|
|
215
|
+
return c.json(command, 201);
|
|
216
|
+
});
|
|
217
|
+
devices.post("/:id/reboot", async (c) => {
|
|
218
|
+
const command = await mdm.devices.reboot(c.req.param("id"));
|
|
219
|
+
return c.json(command, 201);
|
|
220
|
+
});
|
|
221
|
+
devices.post("/:id/lock", async (c) => {
|
|
222
|
+
const body = await c.req.json().catch(() => ({ message: void 0 }));
|
|
223
|
+
const command = await mdm.devices.lock(c.req.param("id"), body.message);
|
|
224
|
+
return c.json(command, 201);
|
|
225
|
+
});
|
|
226
|
+
devices.post("/:id/wipe", async (c) => {
|
|
227
|
+
const body = await c.req.json().catch(() => ({ preserveData: void 0 }));
|
|
228
|
+
const command = await mdm.devices.wipe(c.req.param("id"), body.preserveData);
|
|
229
|
+
return c.json(command, 201);
|
|
230
|
+
});
|
|
231
|
+
app.route("/devices", devices);
|
|
232
|
+
}
|
|
233
|
+
if (routes.policies) {
|
|
234
|
+
const policies = new Hono();
|
|
235
|
+
if (options.enableAuth) {
|
|
236
|
+
policies.use("/*", adminAuth);
|
|
237
|
+
}
|
|
238
|
+
policies.get("/", async (c) => {
|
|
239
|
+
const result = await mdm.policies.list();
|
|
240
|
+
return c.json({ policies: result });
|
|
241
|
+
});
|
|
242
|
+
policies.get("/default", async (c) => {
|
|
243
|
+
const policy = await mdm.policies.getDefault();
|
|
244
|
+
if (!policy) {
|
|
245
|
+
throw new HTTPException(404, { message: "No default policy set" });
|
|
246
|
+
}
|
|
247
|
+
return c.json(policy);
|
|
248
|
+
});
|
|
249
|
+
policies.get("/:id", async (c) => {
|
|
250
|
+
const policy = await mdm.policies.get(c.req.param("id"));
|
|
251
|
+
if (!policy) {
|
|
252
|
+
throw new HTTPException(404, { message: "Policy not found" });
|
|
253
|
+
}
|
|
254
|
+
return c.json(policy);
|
|
255
|
+
});
|
|
256
|
+
policies.post("/", async (c) => {
|
|
257
|
+
const body = await c.req.json();
|
|
258
|
+
const policy = await mdm.policies.create(body);
|
|
259
|
+
return c.json(policy, 201);
|
|
260
|
+
});
|
|
261
|
+
policies.patch("/:id", async (c) => {
|
|
262
|
+
const body = await c.req.json();
|
|
263
|
+
const policy = await mdm.policies.update(c.req.param("id"), body);
|
|
264
|
+
return c.json(policy);
|
|
265
|
+
});
|
|
266
|
+
policies.delete("/:id", async (c) => {
|
|
267
|
+
await mdm.policies.delete(c.req.param("id"));
|
|
268
|
+
return c.json({ status: "ok" });
|
|
269
|
+
});
|
|
270
|
+
policies.post("/:id/default", async (c) => {
|
|
271
|
+
const policy = await mdm.policies.setDefault(c.req.param("id"));
|
|
272
|
+
return c.json(policy);
|
|
273
|
+
});
|
|
274
|
+
policies.get("/:id/devices", async (c) => {
|
|
275
|
+
const devices = await mdm.policies.getDevices(c.req.param("id"));
|
|
276
|
+
return c.json({ devices });
|
|
277
|
+
});
|
|
278
|
+
app.route("/policies", policies);
|
|
279
|
+
}
|
|
280
|
+
if (routes.applications) {
|
|
281
|
+
const applications = new Hono();
|
|
282
|
+
if (options.enableAuth) {
|
|
283
|
+
applications.use("/*", adminAuth);
|
|
284
|
+
}
|
|
285
|
+
applications.get("/", async (c) => {
|
|
286
|
+
const activeOnly = c.req.query("active") === "true";
|
|
287
|
+
const result = await mdm.apps.list(activeOnly);
|
|
288
|
+
return c.json({ applications: result });
|
|
289
|
+
});
|
|
290
|
+
applications.get("/:id", async (c) => {
|
|
291
|
+
const app2 = await mdm.apps.get(c.req.param("id"));
|
|
292
|
+
if (!app2) {
|
|
293
|
+
throw new HTTPException(404, { message: "Application not found" });
|
|
294
|
+
}
|
|
295
|
+
return c.json(app2);
|
|
296
|
+
});
|
|
297
|
+
applications.get("/package/:packageName", async (c) => {
|
|
298
|
+
const version = c.req.query("version");
|
|
299
|
+
const app2 = await mdm.apps.getByPackage(c.req.param("packageName"), version);
|
|
300
|
+
if (!app2) {
|
|
301
|
+
throw new HTTPException(404, { message: "Application not found" });
|
|
302
|
+
}
|
|
303
|
+
return c.json(app2);
|
|
304
|
+
});
|
|
305
|
+
applications.post("/", async (c) => {
|
|
306
|
+
const body = await c.req.json();
|
|
307
|
+
const app2 = await mdm.apps.register(body);
|
|
308
|
+
return c.json(app2, 201);
|
|
309
|
+
});
|
|
310
|
+
applications.patch("/:id", async (c) => {
|
|
311
|
+
const body = await c.req.json();
|
|
312
|
+
const app2 = await mdm.apps.update(c.req.param("id"), body);
|
|
313
|
+
return c.json(app2);
|
|
314
|
+
});
|
|
315
|
+
applications.delete("/:id", async (c) => {
|
|
316
|
+
await mdm.apps.delete(c.req.param("id"));
|
|
317
|
+
return c.json({ status: "ok" });
|
|
318
|
+
});
|
|
319
|
+
applications.post("/:id/activate", async (c) => {
|
|
320
|
+
const app2 = await mdm.apps.activate(c.req.param("id"));
|
|
321
|
+
return c.json(app2);
|
|
322
|
+
});
|
|
323
|
+
applications.post("/:id/deactivate", async (c) => {
|
|
324
|
+
const app2 = await mdm.apps.deactivate(c.req.param("id"));
|
|
325
|
+
return c.json(app2);
|
|
326
|
+
});
|
|
327
|
+
applications.post("/:packageName/deploy", async (c) => {
|
|
328
|
+
const body = await c.req.json();
|
|
329
|
+
await mdm.apps.deploy(c.req.param("packageName"), body);
|
|
330
|
+
return c.json({ status: "ok", message: "Deployment initiated" });
|
|
331
|
+
});
|
|
332
|
+
applications.post("/:packageName/install/:deviceId", async (c) => {
|
|
333
|
+
const version = c.req.query("version");
|
|
334
|
+
const command = await mdm.apps.installOnDevice(
|
|
335
|
+
c.req.param("packageName"),
|
|
336
|
+
c.req.param("deviceId"),
|
|
337
|
+
version
|
|
338
|
+
);
|
|
339
|
+
return c.json(command, 201);
|
|
340
|
+
});
|
|
341
|
+
applications.post("/:packageName/uninstall/:deviceId", async (c) => {
|
|
342
|
+
const command = await mdm.apps.uninstallFromDevice(
|
|
343
|
+
c.req.param("packageName"),
|
|
344
|
+
c.req.param("deviceId")
|
|
345
|
+
);
|
|
346
|
+
return c.json(command, 201);
|
|
347
|
+
});
|
|
348
|
+
app.route("/applications", applications);
|
|
349
|
+
}
|
|
350
|
+
if (routes.groups) {
|
|
351
|
+
const groups = new Hono();
|
|
352
|
+
if (options.enableAuth) {
|
|
353
|
+
groups.use("/*", adminAuth);
|
|
354
|
+
}
|
|
355
|
+
groups.get("/", async (c) => {
|
|
356
|
+
const result = await mdm.groups.list();
|
|
357
|
+
return c.json({ groups: result });
|
|
358
|
+
});
|
|
359
|
+
groups.get("/:id", async (c) => {
|
|
360
|
+
const group = await mdm.groups.get(c.req.param("id"));
|
|
361
|
+
if (!group) {
|
|
362
|
+
throw new HTTPException(404, { message: "Group not found" });
|
|
363
|
+
}
|
|
364
|
+
return c.json(group);
|
|
365
|
+
});
|
|
366
|
+
groups.post("/", async (c) => {
|
|
367
|
+
const body = await c.req.json();
|
|
368
|
+
const group = await mdm.groups.create(body);
|
|
369
|
+
return c.json(group, 201);
|
|
370
|
+
});
|
|
371
|
+
groups.patch("/:id", async (c) => {
|
|
372
|
+
const body = await c.req.json();
|
|
373
|
+
const group = await mdm.groups.update(c.req.param("id"), body);
|
|
374
|
+
return c.json(group);
|
|
375
|
+
});
|
|
376
|
+
groups.delete("/:id", async (c) => {
|
|
377
|
+
await mdm.groups.delete(c.req.param("id"));
|
|
378
|
+
return c.json({ status: "ok" });
|
|
379
|
+
});
|
|
380
|
+
groups.get("/:id/devices", async (c) => {
|
|
381
|
+
const devices = await mdm.groups.getDevices(c.req.param("id"));
|
|
382
|
+
return c.json({ devices });
|
|
383
|
+
});
|
|
384
|
+
groups.post("/:id/devices", async (c) => {
|
|
385
|
+
const { deviceId } = await c.req.json();
|
|
386
|
+
await mdm.groups.addDevice(c.req.param("id"), deviceId);
|
|
387
|
+
return c.json({ status: "ok" });
|
|
388
|
+
});
|
|
389
|
+
groups.delete("/:id/devices/:deviceId", async (c) => {
|
|
390
|
+
await mdm.groups.removeDevice(c.req.param("id"), c.req.param("deviceId"));
|
|
391
|
+
return c.json({ status: "ok" });
|
|
392
|
+
});
|
|
393
|
+
groups.get("/:id/children", async (c) => {
|
|
394
|
+
const children = await mdm.groups.getChildren(c.req.param("id"));
|
|
395
|
+
return c.json({ groups: children });
|
|
396
|
+
});
|
|
397
|
+
app.route("/groups", groups);
|
|
398
|
+
}
|
|
399
|
+
if (routes.commands) {
|
|
400
|
+
const commands = new Hono();
|
|
401
|
+
if (options.enableAuth) {
|
|
402
|
+
commands.use("/*", adminAuth);
|
|
403
|
+
}
|
|
404
|
+
commands.get("/", async (c) => {
|
|
405
|
+
const filter = {
|
|
406
|
+
deviceId: c.req.query("deviceId"),
|
|
407
|
+
status: c.req.query("status"),
|
|
408
|
+
type: c.req.query("type"),
|
|
409
|
+
limit: c.req.query("limit") ? parseInt(c.req.query("limit")) : void 0,
|
|
410
|
+
offset: c.req.query("offset") ? parseInt(c.req.query("offset")) : void 0
|
|
411
|
+
};
|
|
412
|
+
const result = await mdm.commands.list(filter);
|
|
413
|
+
return c.json({ commands: result });
|
|
414
|
+
});
|
|
415
|
+
commands.get("/:id", async (c) => {
|
|
416
|
+
const command = await mdm.commands.get(c.req.param("id"));
|
|
417
|
+
if (!command) {
|
|
418
|
+
throw new HTTPException(404, { message: "Command not found" });
|
|
419
|
+
}
|
|
420
|
+
return c.json(command);
|
|
421
|
+
});
|
|
422
|
+
commands.post("/", async (c) => {
|
|
423
|
+
const body = await c.req.json();
|
|
424
|
+
const command = await mdm.commands.send(body);
|
|
425
|
+
return c.json(command, 201);
|
|
426
|
+
});
|
|
427
|
+
commands.post("/:id/cancel", async (c) => {
|
|
428
|
+
const command = await mdm.commands.cancel(c.req.param("id"));
|
|
429
|
+
return c.json(command);
|
|
430
|
+
});
|
|
431
|
+
app.route("/commands", commands);
|
|
432
|
+
}
|
|
433
|
+
if (routes.events) {
|
|
434
|
+
const events = new Hono();
|
|
435
|
+
if (options.enableAuth) {
|
|
436
|
+
events.use("/*", adminAuth);
|
|
437
|
+
}
|
|
438
|
+
events.get("/", async (c) => {
|
|
439
|
+
const filter = {
|
|
440
|
+
deviceId: c.req.query("deviceId"),
|
|
441
|
+
type: c.req.query("type"),
|
|
442
|
+
limit: c.req.query("limit") ? parseInt(c.req.query("limit")) : void 0,
|
|
443
|
+
offset: c.req.query("offset") ? parseInt(c.req.query("offset")) : void 0
|
|
444
|
+
};
|
|
445
|
+
const result = await mdm.db.listEvents(filter);
|
|
446
|
+
return c.json({ events: result });
|
|
447
|
+
});
|
|
448
|
+
app.route("/events", events);
|
|
449
|
+
}
|
|
450
|
+
app.get("/health", (c) => {
|
|
451
|
+
return c.json({
|
|
452
|
+
status: "ok",
|
|
453
|
+
version: "0.1.0",
|
|
454
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
return app;
|
|
458
|
+
}
|
|
459
|
+
export {
|
|
460
|
+
honoAdapter
|
|
461
|
+
};
|
|
462
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * OpenMDM Hono Adapter\n *\n * HTTP routes adapter for Hono framework.\n *\n * @example\n * ```typescript\n * import { Hono } from 'hono';\n * import { createMDM } from '@openmdm/core';\n * import { honoAdapter } from '@openmdm/hono';\n *\n * const mdm = createMDM({ ... });\n * const app = new Hono<MDMEnv>();\n *\n * // Mount MDM routes\n * app.route('/mdm', honoAdapter(mdm));\n * ```\n */\n\nimport { Hono } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { Context, MiddlewareHandler, Env } from 'hono';\nimport type {\n MDMInstance,\n EnrollmentRequest,\n Heartbeat,\n DeviceFilter,\n CommandFilter,\n CreatePolicyInput,\n UpdatePolicyInput,\n CreateApplicationInput,\n UpdateApplicationInput,\n CreateGroupInput,\n UpdateGroupInput,\n SendCommandInput,\n MDMError,\n AuthenticationError,\n AuthorizationError,\n} from '@openmdm/core';\n\n/**\n * Context variables set by OpenMDM middlewares\n */\ninterface MDMVariables {\n deviceId?: string;\n user?: unknown;\n}\n\n/**\n * Hono environment type for OpenMDM routes\n */\ntype MDMEnv = {\n Variables: MDMVariables;\n};\n\nexport interface HonoAdapterOptions {\n /**\n * Base path prefix for all routes (default: '')\n */\n basePath?: string;\n\n /**\n * Enable authentication middleware for admin routes\n */\n enableAuth?: boolean;\n\n /**\n * Custom error handler\n */\n onError?: (error: Error, c: Context) => Response | Promise<Response>;\n\n /**\n * Routes to expose (default: all)\n */\n routes?: {\n enrollment?: boolean;\n devices?: boolean;\n policies?: boolean;\n applications?: boolean;\n groups?: boolean;\n commands?: boolean;\n events?: boolean;\n };\n}\n\n/**\n * Create a Hono router with OpenMDM API routes\n */\nexport function honoAdapter(\n mdm: MDMInstance,\n options: HonoAdapterOptions = {}\n): Hono<MDMEnv> {\n const app = new Hono<MDMEnv>();\n\n const routes = {\n enrollment: true,\n devices: true,\n policies: true,\n applications: true,\n groups: true,\n commands: true,\n events: true,\n ...options.routes,\n };\n\n // Error handling middleware\n app.onError((error, c) => {\n if (options.onError) {\n return options.onError(error, c);\n }\n\n console.error('[OpenMDM] Error:', error);\n\n if (error instanceof HTTPException) {\n return c.json({ error: error.message }, error.status);\n }\n\n const mdmError = error as MDMError;\n if (mdmError.code && mdmError.statusCode) {\n return c.json(\n {\n error: mdmError.message,\n code: mdmError.code,\n details: mdmError.details,\n },\n mdmError.statusCode as any\n );\n }\n\n return c.json({ error: 'Internal server error' }, 500);\n });\n\n // Device authentication middleware\n const deviceAuth: MiddlewareHandler = async (c, next) => {\n const token = c.req.header('Authorization')?.replace('Bearer ', '');\n const deviceId = c.req.header('X-Device-Id');\n\n if (!token && !deviceId) {\n throw new HTTPException(401, { message: 'Device authentication required' });\n }\n\n if (token) {\n const result = await mdm.verifyDeviceToken(token);\n if (!result) {\n throw new HTTPException(401, { message: 'Invalid device token' });\n }\n c.set('deviceId', result.deviceId);\n } else if (deviceId) {\n c.set('deviceId', deviceId);\n }\n\n await next();\n };\n\n // Admin authentication middleware\n const adminAuth: MiddlewareHandler = async (c, next) => {\n if (!mdm.config.auth) {\n await next();\n return;\n }\n\n const user = await mdm.config.auth.getUser(c);\n if (!user) {\n throw new HTTPException(401, { message: 'Authentication required' });\n }\n\n if (mdm.config.auth.isAdmin) {\n const isAdmin = await mdm.config.auth.isAdmin(user);\n if (!isAdmin) {\n throw new HTTPException(403, { message: 'Admin access required' });\n }\n }\n\n c.set('user', user);\n await next();\n };\n\n // ============================================\n // Enrollment Routes (Device-facing)\n // ============================================\n\n if (routes.enrollment) {\n const enrollment = new Hono<MDMEnv>();\n\n // Enroll device\n enrollment.post('/enroll', async (c) => {\n const body = await c.req.json<EnrollmentRequest>();\n\n // Validate required fields\n if (!body.model || !body.manufacturer || !body.osVersion) {\n throw new HTTPException(400, {\n message: 'Missing required fields: model, manufacturer, osVersion',\n });\n }\n\n if (!body.macAddress && !body.serialNumber && !body.imei && !body.androidId) {\n throw new HTTPException(400, {\n message: 'At least one device identifier required',\n });\n }\n\n const result = await mdm.enroll(body);\n\n // Add server URL from request if not configured\n if (!result.serverUrl) {\n const url = new URL(c.req.url);\n result.serverUrl = `${url.protocol}//${url.host}`;\n }\n\n return c.json(result, 201);\n });\n\n // Device heartbeat\n enrollment.post('/heartbeat', deviceAuth, async (c) => {\n const deviceId = c.get('deviceId') as string;\n const body = await c.req.json<Omit<Heartbeat, 'deviceId'>>();\n\n await mdm.processHeartbeat(deviceId, {\n ...body,\n deviceId,\n timestamp: new Date(body.timestamp || Date.now()),\n });\n\n // Return pending commands for the device\n const pendingCommands = await mdm.commands.getPending(deviceId);\n\n return c.json({\n status: 'ok',\n commands: pendingCommands,\n });\n });\n\n // Get device config/policy\n enrollment.get('/config', deviceAuth, async (c) => {\n const deviceId = c.get('deviceId') as string;\n const device = await mdm.devices.get(deviceId);\n\n if (!device) {\n throw new HTTPException(404, { message: 'Device not found' });\n }\n\n let policy = null;\n if (device.policyId) {\n policy = await mdm.policies.get(device.policyId);\n } else {\n policy = await mdm.policies.getDefault();\n }\n\n return c.json({\n device: {\n id: device.id,\n enrollmentId: device.enrollmentId,\n status: device.status,\n },\n policy,\n });\n });\n\n // Register push token\n enrollment.post('/push-token', deviceAuth, async (c) => {\n const deviceId = c.get('deviceId') as string;\n const body = await c.req.json<{ provider: string; token: string }>();\n\n await mdm.db.upsertPushToken({\n deviceId,\n provider: body.provider as any,\n token: body.token,\n });\n\n return c.json({ status: 'ok' });\n });\n\n // Acknowledge command\n enrollment.post('/commands/:id/ack', deviceAuth, async (c) => {\n const commandId = c.req.param('id');\n const command = await mdm.commands.acknowledge(commandId);\n return c.json(command);\n });\n\n // Complete command\n enrollment.post('/commands/:id/complete', deviceAuth, async (c) => {\n const commandId = c.req.param('id');\n const body = await c.req.json<{ success: boolean; message?: string; data?: unknown }>();\n const command = await mdm.commands.complete(commandId, body);\n return c.json(command);\n });\n\n // Fail command\n enrollment.post('/commands/:id/fail', deviceAuth, async (c) => {\n const commandId = c.req.param('id');\n const body = await c.req.json<{ error: string }>();\n const command = await mdm.commands.fail(commandId, body.error);\n return c.json(command);\n });\n\n app.route('/agent', enrollment);\n }\n\n // ============================================\n // Device Routes (Admin-facing)\n // ============================================\n\n if (routes.devices) {\n const devices = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n devices.use('/*', adminAuth);\n }\n\n // List devices\n devices.get('/', async (c) => {\n const filter: DeviceFilter = {\n status: c.req.query('status') as any,\n policyId: c.req.query('policyId'),\n groupId: c.req.query('groupId'),\n search: c.req.query('search'),\n limit: c.req.query('limit') ? parseInt(c.req.query('limit')!) : undefined,\n offset: c.req.query('offset') ? parseInt(c.req.query('offset')!) : undefined,\n };\n\n const result = await mdm.devices.list(filter);\n return c.json(result);\n });\n\n // Get device\n devices.get('/:id', async (c) => {\n const device = await mdm.devices.get(c.req.param('id'));\n if (!device) {\n throw new HTTPException(404, { message: 'Device not found' });\n }\n return c.json(device);\n });\n\n // Update device\n devices.patch('/:id', async (c) => {\n const body = await c.req.json();\n const device = await mdm.devices.update(c.req.param('id'), body);\n return c.json(device);\n });\n\n // Delete device\n devices.delete('/:id', async (c) => {\n await mdm.devices.delete(c.req.param('id'));\n return c.json({ status: 'ok' });\n });\n\n // Assign policy to device\n devices.post('/:id/policy', async (c) => {\n const { policyId } = await c.req.json<{ policyId: string | null }>();\n const device = await mdm.devices.assignPolicy(c.req.param('id'), policyId);\n return c.json(device);\n });\n\n // Get device groups\n devices.get('/:id/groups', async (c) => {\n const groups = await mdm.devices.getGroups(c.req.param('id'));\n return c.json({ groups });\n });\n\n // Add device to group\n devices.post('/:id/groups', async (c) => {\n const { groupId } = await c.req.json<{ groupId: string }>();\n await mdm.devices.addToGroup(c.req.param('id'), groupId);\n return c.json({ status: 'ok' });\n });\n\n // Remove device from group\n devices.delete('/:id/groups/:groupId', async (c) => {\n await mdm.devices.removeFromGroup(c.req.param('id'), c.req.param('groupId'));\n return c.json({ status: 'ok' });\n });\n\n // Send command to device\n devices.post('/:id/commands', async (c) => {\n const body = await c.req.json<Omit<SendCommandInput, 'deviceId'>>();\n const command = await mdm.devices.sendCommand(c.req.param('id'), body);\n return c.json(command, 201);\n });\n\n // Convenience: Sync device\n devices.post('/:id/sync', async (c) => {\n const command = await mdm.devices.sync(c.req.param('id'));\n return c.json(command, 201);\n });\n\n // Convenience: Reboot device\n devices.post('/:id/reboot', async (c) => {\n const command = await mdm.devices.reboot(c.req.param('id'));\n return c.json(command, 201);\n });\n\n // Convenience: Lock device\n devices.post('/:id/lock', async (c) => {\n const body = await c.req.json<{ message?: string }>().catch(() => ({ message: undefined }));\n const command = await mdm.devices.lock(c.req.param('id'), body.message);\n return c.json(command, 201);\n });\n\n // Convenience: Wipe device\n devices.post('/:id/wipe', async (c) => {\n const body = await c.req.json<{ preserveData?: boolean }>().catch(() => ({ preserveData: undefined }));\n const command = await mdm.devices.wipe(c.req.param('id'), body.preserveData);\n return c.json(command, 201);\n });\n\n app.route('/devices', devices);\n }\n\n // ============================================\n // Policy Routes\n // ============================================\n\n if (routes.policies) {\n const policies = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n policies.use('/*', adminAuth);\n }\n\n // List policies\n policies.get('/', async (c) => {\n const result = await mdm.policies.list();\n return c.json({ policies: result });\n });\n\n // Get default policy\n policies.get('/default', async (c) => {\n const policy = await mdm.policies.getDefault();\n if (!policy) {\n throw new HTTPException(404, { message: 'No default policy set' });\n }\n return c.json(policy);\n });\n\n // Get policy\n policies.get('/:id', async (c) => {\n const policy = await mdm.policies.get(c.req.param('id'));\n if (!policy) {\n throw new HTTPException(404, { message: 'Policy not found' });\n }\n return c.json(policy);\n });\n\n // Create policy\n policies.post('/', async (c) => {\n const body = await c.req.json<CreatePolicyInput>();\n const policy = await mdm.policies.create(body);\n return c.json(policy, 201);\n });\n\n // Update policy\n policies.patch('/:id', async (c) => {\n const body = await c.req.json<UpdatePolicyInput>();\n const policy = await mdm.policies.update(c.req.param('id'), body);\n return c.json(policy);\n });\n\n // Delete policy\n policies.delete('/:id', async (c) => {\n await mdm.policies.delete(c.req.param('id'));\n return c.json({ status: 'ok' });\n });\n\n // Set default policy\n policies.post('/:id/default', async (c) => {\n const policy = await mdm.policies.setDefault(c.req.param('id'));\n return c.json(policy);\n });\n\n // Get devices with this policy\n policies.get('/:id/devices', async (c) => {\n const devices = await mdm.policies.getDevices(c.req.param('id'));\n return c.json({ devices });\n });\n\n app.route('/policies', policies);\n }\n\n // ============================================\n // Application Routes\n // ============================================\n\n if (routes.applications) {\n const applications = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n applications.use('/*', adminAuth);\n }\n\n // List applications\n applications.get('/', async (c) => {\n const activeOnly = c.req.query('active') === 'true';\n const result = await mdm.apps.list(activeOnly);\n return c.json({ applications: result });\n });\n\n // Get application by ID\n applications.get('/:id', async (c) => {\n const app = await mdm.apps.get(c.req.param('id'));\n if (!app) {\n throw new HTTPException(404, { message: 'Application not found' });\n }\n return c.json(app);\n });\n\n // Get application by package name\n applications.get('/package/:packageName', async (c) => {\n const version = c.req.query('version');\n const app = await mdm.apps.getByPackage(c.req.param('packageName'), version);\n if (!app) {\n throw new HTTPException(404, { message: 'Application not found' });\n }\n return c.json(app);\n });\n\n // Register application\n applications.post('/', async (c) => {\n const body = await c.req.json<CreateApplicationInput>();\n const app = await mdm.apps.register(body);\n return c.json(app, 201);\n });\n\n // Update application\n applications.patch('/:id', async (c) => {\n const body = await c.req.json<UpdateApplicationInput>();\n const app = await mdm.apps.update(c.req.param('id'), body);\n return c.json(app);\n });\n\n // Delete application\n applications.delete('/:id', async (c) => {\n await mdm.apps.delete(c.req.param('id'));\n return c.json({ status: 'ok' });\n });\n\n // Activate application\n applications.post('/:id/activate', async (c) => {\n const app = await mdm.apps.activate(c.req.param('id'));\n return c.json(app);\n });\n\n // Deactivate application\n applications.post('/:id/deactivate', async (c) => {\n const app = await mdm.apps.deactivate(c.req.param('id'));\n return c.json(app);\n });\n\n // Deploy application\n applications.post('/:packageName/deploy', async (c) => {\n const body = await c.req.json<{\n devices?: string[];\n policies?: string[];\n groups?: string[];\n }>();\n await mdm.apps.deploy(c.req.param('packageName'), body);\n return c.json({ status: 'ok', message: 'Deployment initiated' });\n });\n\n // Install app on device\n applications.post('/:packageName/install/:deviceId', async (c) => {\n const version = c.req.query('version');\n const command = await mdm.apps.installOnDevice(\n c.req.param('packageName'),\n c.req.param('deviceId'),\n version\n );\n return c.json(command, 201);\n });\n\n // Uninstall app from device\n applications.post('/:packageName/uninstall/:deviceId', async (c) => {\n const command = await mdm.apps.uninstallFromDevice(\n c.req.param('packageName'),\n c.req.param('deviceId')\n );\n return c.json(command, 201);\n });\n\n app.route('/applications', applications);\n }\n\n // ============================================\n // Group Routes\n // ============================================\n\n if (routes.groups) {\n const groups = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n groups.use('/*', adminAuth);\n }\n\n // List groups\n groups.get('/', async (c) => {\n const result = await mdm.groups.list();\n return c.json({ groups: result });\n });\n\n // Get group\n groups.get('/:id', async (c) => {\n const group = await mdm.groups.get(c.req.param('id'));\n if (!group) {\n throw new HTTPException(404, { message: 'Group not found' });\n }\n return c.json(group);\n });\n\n // Create group\n groups.post('/', async (c) => {\n const body = await c.req.json<CreateGroupInput>();\n const group = await mdm.groups.create(body);\n return c.json(group, 201);\n });\n\n // Update group\n groups.patch('/:id', async (c) => {\n const body = await c.req.json<UpdateGroupInput>();\n const group = await mdm.groups.update(c.req.param('id'), body);\n return c.json(group);\n });\n\n // Delete group\n groups.delete('/:id', async (c) => {\n await mdm.groups.delete(c.req.param('id'));\n return c.json({ status: 'ok' });\n });\n\n // Get devices in group\n groups.get('/:id/devices', async (c) => {\n const devices = await mdm.groups.getDevices(c.req.param('id'));\n return c.json({ devices });\n });\n\n // Add device to group\n groups.post('/:id/devices', async (c) => {\n const { deviceId } = await c.req.json<{ deviceId: string }>();\n await mdm.groups.addDevice(c.req.param('id'), deviceId);\n return c.json({ status: 'ok' });\n });\n\n // Remove device from group\n groups.delete('/:id/devices/:deviceId', async (c) => {\n await mdm.groups.removeDevice(c.req.param('id'), c.req.param('deviceId'));\n return c.json({ status: 'ok' });\n });\n\n // Get child groups\n groups.get('/:id/children', async (c) => {\n const children = await mdm.groups.getChildren(c.req.param('id'));\n return c.json({ groups: children });\n });\n\n app.route('/groups', groups);\n }\n\n // ============================================\n // Command Routes\n // ============================================\n\n if (routes.commands) {\n const commands = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n commands.use('/*', adminAuth);\n }\n\n // List commands\n commands.get('/', async (c) => {\n const filter: CommandFilter = {\n deviceId: c.req.query('deviceId'),\n status: c.req.query('status') as any,\n type: c.req.query('type') as any,\n limit: c.req.query('limit') ? parseInt(c.req.query('limit')!) : undefined,\n offset: c.req.query('offset') ? parseInt(c.req.query('offset')!) : undefined,\n };\n\n const result = await mdm.commands.list(filter);\n return c.json({ commands: result });\n });\n\n // Get command\n commands.get('/:id', async (c) => {\n const command = await mdm.commands.get(c.req.param('id'));\n if (!command) {\n throw new HTTPException(404, { message: 'Command not found' });\n }\n return c.json(command);\n });\n\n // Send command\n commands.post('/', async (c) => {\n const body = await c.req.json<SendCommandInput>();\n const command = await mdm.commands.send(body);\n return c.json(command, 201);\n });\n\n // Cancel command\n commands.post('/:id/cancel', async (c) => {\n const command = await mdm.commands.cancel(c.req.param('id'));\n return c.json(command);\n });\n\n app.route('/commands', commands);\n }\n\n // ============================================\n // Event Routes\n // ============================================\n\n if (routes.events) {\n const events = new Hono<MDMEnv>();\n\n if (options.enableAuth) {\n events.use('/*', adminAuth);\n }\n\n // List events\n events.get('/', async (c) => {\n const filter = {\n deviceId: c.req.query('deviceId'),\n type: c.req.query('type') as any,\n limit: c.req.query('limit') ? parseInt(c.req.query('limit')!) : undefined,\n offset: c.req.query('offset') ? parseInt(c.req.query('offset')!) : undefined,\n };\n\n const result = await mdm.db.listEvents(filter);\n return c.json({ events: result });\n });\n\n app.route('/events', events);\n }\n\n // ============================================\n // Health Check\n // ============================================\n\n app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n version: '0.1.0',\n timestamp: new Date().toISOString(),\n });\n });\n\n return app;\n}\n"],"mappings":"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","names":["app"]}
|