@openmdm/drizzle-adapter 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/chunk-ULFOOXEW.js +11 -0
- package/dist/chunk-ULFOOXEW.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +588 -0
- package/dist/index.js.map +1 -0
- package/dist/mysql.d.ts +16 -0
- package/dist/mysql.js +9 -0
- package/dist/mysql.js.map +1 -0
- package/dist/postgres.d.ts +3505 -0
- package/dist/postgres.js +374 -0
- package/dist/postgres.js.map +1 -0
- package/dist/schema.d.ts +28 -0
- package/dist/schema.js +13 -0
- package/dist/schema.js.map +1 -0
- package/dist/sqlite.d.ts +16 -0
- package/dist/sqlite.js +9 -0
- package/dist/sqlite.js.map +1 -0
- package/package.json +75 -0
- package/src/index.ts +914 -0
- package/src/mysql.ts +24 -0
- package/src/postgres.ts +487 -0
- package/src/schema.ts +27 -0
- package/src/sqlite.ts +24 -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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
import { mdmSchema, getTableNames, getColumnNames } from "@openmdm/core/schema";
|
|
3
|
+
var DEFAULT_TABLE_PREFIX = "mdm_";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
DEFAULT_TABLE_PREFIX,
|
|
7
|
+
mdmSchema,
|
|
8
|
+
getTableNames,
|
|
9
|
+
getColumnNames
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-ULFOOXEW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * OpenMDM Drizzle Schema\n *\n * This file exports the schema definition for use with schema generators.\n * For ready-to-use Drizzle tables, import from:\n * - @openmdm/drizzle-adapter/postgres\n * - @openmdm/drizzle-adapter/mysql\n * - @openmdm/drizzle-adapter/sqlite\n */\n\nexport { mdmSchema, getTableNames, getColumnNames } from '@openmdm/core/schema';\n\n/**\n * Table prefix for MDM tables.\n * Can be customized via the adapter options.\n */\nexport const DEFAULT_TABLE_PREFIX = 'mdm_';\n\n/**\n * Schema options for customizing table generation\n */\nexport interface SchemaOptions {\n /** Prefix for all MDM tables (default: 'mdm_') */\n tablePrefix?: string;\n /** Custom schema name (PostgreSQL only) */\n schema?: string;\n}\n"],"mappings":";AAUA,SAAS,WAAW,eAAe,sBAAsB;AAMlD,IAAM,uBAAuB;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DatabaseAdapter } from '@openmdm/core';
|
|
2
|
+
import { mdmDevices, mdmPolicies, mdmApplications, mdmCommands, mdmEvents, mdmGroups, mdmDeviceGroups, mdmPushTokens } from './postgres.js';
|
|
3
|
+
export { DEFAULT_TABLE_PREFIX, SchemaOptions } from './schema.js';
|
|
4
|
+
import 'drizzle-orm';
|
|
5
|
+
import 'drizzle-orm/pg-core';
|
|
6
|
+
import '@openmdm/core/schema';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* OpenMDM Drizzle Adapter
|
|
10
|
+
*
|
|
11
|
+
* Database adapter for Drizzle ORM, supporting PostgreSQL, MySQL, and SQLite.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { drizzle } from 'drizzle-orm/node-postgres';
|
|
16
|
+
* import { drizzleAdapter } from '@openmdm/drizzle-adapter';
|
|
17
|
+
* import { mdmSchema } from '@openmdm/drizzle-adapter/postgres';
|
|
18
|
+
* import { createMDM } from '@openmdm/core';
|
|
19
|
+
*
|
|
20
|
+
* const pool = new Pool({ connectionString: DATABASE_URL });
|
|
21
|
+
* const db = drizzle(pool, { schema: mdmSchema });
|
|
22
|
+
*
|
|
23
|
+
* const mdm = createMDM({
|
|
24
|
+
* database: drizzleAdapter(db),
|
|
25
|
+
* // ...
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
type DrizzleDB = {
|
|
31
|
+
select: (columns?: unknown) => unknown;
|
|
32
|
+
insert: (table: unknown) => unknown;
|
|
33
|
+
update: (table: unknown) => unknown;
|
|
34
|
+
delete: (table: unknown) => unknown;
|
|
35
|
+
query: Record<string, unknown>;
|
|
36
|
+
transaction: <T>(fn: (tx: DrizzleDB) => Promise<T>) => Promise<T>;
|
|
37
|
+
};
|
|
38
|
+
interface DrizzleAdapterOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Table references - pass your imported Drizzle tables
|
|
41
|
+
*/
|
|
42
|
+
tables: {
|
|
43
|
+
devices: typeof mdmDevices;
|
|
44
|
+
policies: typeof mdmPolicies;
|
|
45
|
+
applications: typeof mdmApplications;
|
|
46
|
+
commands: typeof mdmCommands;
|
|
47
|
+
events: typeof mdmEvents;
|
|
48
|
+
groups: typeof mdmGroups;
|
|
49
|
+
deviceGroups: typeof mdmDeviceGroups;
|
|
50
|
+
pushTokens: typeof mdmPushTokens;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create a Drizzle database adapter for OpenMDM
|
|
55
|
+
*/
|
|
56
|
+
declare function drizzleAdapter(db: DrizzleDB, options: DrizzleAdapterOptions): DatabaseAdapter;
|
|
57
|
+
|
|
58
|
+
export { type DrizzleAdapterOptions, drizzleAdapter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_TABLE_PREFIX
|
|
3
|
+
} from "./chunk-ULFOOXEW.js";
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
import { eq, and, or, like, inArray, desc, sql } from "drizzle-orm";
|
|
7
|
+
import { nanoid } from "nanoid";
|
|
8
|
+
function drizzleAdapter(db, options) {
|
|
9
|
+
const { tables } = options;
|
|
10
|
+
const {
|
|
11
|
+
devices,
|
|
12
|
+
policies,
|
|
13
|
+
applications,
|
|
14
|
+
commands,
|
|
15
|
+
events,
|
|
16
|
+
groups,
|
|
17
|
+
deviceGroups,
|
|
18
|
+
pushTokens
|
|
19
|
+
} = tables;
|
|
20
|
+
const generateId = () => nanoid(21);
|
|
21
|
+
const toDevice = (row) => ({
|
|
22
|
+
id: row.id,
|
|
23
|
+
externalId: row.externalId,
|
|
24
|
+
enrollmentId: row.enrollmentId,
|
|
25
|
+
status: row.status,
|
|
26
|
+
model: row.model,
|
|
27
|
+
manufacturer: row.manufacturer,
|
|
28
|
+
osVersion: row.osVersion,
|
|
29
|
+
serialNumber: row.serialNumber,
|
|
30
|
+
imei: row.imei,
|
|
31
|
+
macAddress: row.macAddress,
|
|
32
|
+
androidId: row.androidId,
|
|
33
|
+
policyId: row.policyId,
|
|
34
|
+
lastHeartbeat: row.lastHeartbeat,
|
|
35
|
+
lastSync: row.lastSync,
|
|
36
|
+
batteryLevel: row.batteryLevel,
|
|
37
|
+
storageUsed: row.storageUsed,
|
|
38
|
+
storageTotal: row.storageTotal,
|
|
39
|
+
location: row.latitude && row.longitude ? {
|
|
40
|
+
latitude: parseFloat(row.latitude),
|
|
41
|
+
longitude: parseFloat(row.longitude),
|
|
42
|
+
timestamp: row.locationTimestamp
|
|
43
|
+
} : null,
|
|
44
|
+
installedApps: row.installedApps,
|
|
45
|
+
tags: row.tags,
|
|
46
|
+
metadata: row.metadata,
|
|
47
|
+
createdAt: row.createdAt,
|
|
48
|
+
updatedAt: row.updatedAt
|
|
49
|
+
});
|
|
50
|
+
const toPolicy = (row) => ({
|
|
51
|
+
id: row.id,
|
|
52
|
+
name: row.name,
|
|
53
|
+
description: row.description,
|
|
54
|
+
isDefault: row.isDefault,
|
|
55
|
+
settings: row.settings,
|
|
56
|
+
createdAt: row.createdAt,
|
|
57
|
+
updatedAt: row.updatedAt
|
|
58
|
+
});
|
|
59
|
+
const toApplication = (row) => ({
|
|
60
|
+
id: row.id,
|
|
61
|
+
name: row.name,
|
|
62
|
+
packageName: row.packageName,
|
|
63
|
+
version: row.version,
|
|
64
|
+
versionCode: row.versionCode,
|
|
65
|
+
url: row.url,
|
|
66
|
+
hash: row.hash,
|
|
67
|
+
size: row.size,
|
|
68
|
+
minSdkVersion: row.minSdkVersion,
|
|
69
|
+
showIcon: row.showIcon,
|
|
70
|
+
runAfterInstall: row.runAfterInstall,
|
|
71
|
+
runAtBoot: row.runAtBoot,
|
|
72
|
+
isSystem: row.isSystem,
|
|
73
|
+
isActive: row.isActive,
|
|
74
|
+
metadata: row.metadata,
|
|
75
|
+
createdAt: row.createdAt,
|
|
76
|
+
updatedAt: row.updatedAt
|
|
77
|
+
});
|
|
78
|
+
const toCommand = (row) => ({
|
|
79
|
+
id: row.id,
|
|
80
|
+
deviceId: row.deviceId,
|
|
81
|
+
type: row.type,
|
|
82
|
+
payload: row.payload,
|
|
83
|
+
status: row.status,
|
|
84
|
+
result: row.result,
|
|
85
|
+
error: row.error,
|
|
86
|
+
createdAt: row.createdAt,
|
|
87
|
+
sentAt: row.sentAt,
|
|
88
|
+
acknowledgedAt: row.acknowledgedAt,
|
|
89
|
+
completedAt: row.completedAt
|
|
90
|
+
});
|
|
91
|
+
const toEvent = (row) => ({
|
|
92
|
+
id: row.id,
|
|
93
|
+
deviceId: row.deviceId,
|
|
94
|
+
type: row.type,
|
|
95
|
+
payload: row.payload,
|
|
96
|
+
createdAt: row.createdAt
|
|
97
|
+
});
|
|
98
|
+
const toGroup = (row) => ({
|
|
99
|
+
id: row.id,
|
|
100
|
+
name: row.name,
|
|
101
|
+
description: row.description,
|
|
102
|
+
policyId: row.policyId,
|
|
103
|
+
parentId: row.parentId,
|
|
104
|
+
metadata: row.metadata,
|
|
105
|
+
createdAt: row.createdAt,
|
|
106
|
+
updatedAt: row.updatedAt
|
|
107
|
+
});
|
|
108
|
+
const toPushToken = (row) => ({
|
|
109
|
+
id: row.id,
|
|
110
|
+
deviceId: row.deviceId,
|
|
111
|
+
provider: row.provider,
|
|
112
|
+
token: row.token,
|
|
113
|
+
isActive: row.isActive,
|
|
114
|
+
createdAt: row.createdAt,
|
|
115
|
+
updatedAt: row.updatedAt
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
// ============================================
|
|
119
|
+
// Device Methods
|
|
120
|
+
// ============================================
|
|
121
|
+
async findDevice(id) {
|
|
122
|
+
const result = await db.select().from(devices).where(eq(devices.id, id)).limit(1);
|
|
123
|
+
return result[0] ? toDevice(result[0]) : null;
|
|
124
|
+
},
|
|
125
|
+
async findDeviceByEnrollmentId(enrollmentId) {
|
|
126
|
+
const result = await db.select().from(devices).where(eq(devices.enrollmentId, enrollmentId)).limit(1);
|
|
127
|
+
return result[0] ? toDevice(result[0]) : null;
|
|
128
|
+
},
|
|
129
|
+
async listDevices(filter) {
|
|
130
|
+
const limit = filter?.limit ?? 100;
|
|
131
|
+
const offset = filter?.offset ?? 0;
|
|
132
|
+
let query = db.select().from(devices);
|
|
133
|
+
const conditions = [];
|
|
134
|
+
if (filter?.status) {
|
|
135
|
+
if (Array.isArray(filter.status)) {
|
|
136
|
+
conditions.push(inArray(devices.status, filter.status));
|
|
137
|
+
} else {
|
|
138
|
+
conditions.push(eq(devices.status, filter.status));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (filter?.policyId) {
|
|
142
|
+
conditions.push(eq(devices.policyId, filter.policyId));
|
|
143
|
+
}
|
|
144
|
+
if (filter?.search) {
|
|
145
|
+
const searchPattern = `%${filter.search}%`;
|
|
146
|
+
conditions.push(
|
|
147
|
+
or(
|
|
148
|
+
like(devices.model, searchPattern),
|
|
149
|
+
like(devices.manufacturer, searchPattern),
|
|
150
|
+
like(devices.enrollmentId, searchPattern),
|
|
151
|
+
like(devices.serialNumber, searchPattern)
|
|
152
|
+
)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (conditions.length > 0) {
|
|
156
|
+
query = query.where(and(...conditions));
|
|
157
|
+
}
|
|
158
|
+
const countResult = await db.select({ count: sql`count(*)` }).from(devices).where(conditions.length > 0 ? and(...conditions) : void 0);
|
|
159
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
160
|
+
const result = await query.orderBy(desc(devices.createdAt)).limit(limit).offset(offset);
|
|
161
|
+
return {
|
|
162
|
+
devices: result.map(toDevice),
|
|
163
|
+
total,
|
|
164
|
+
limit,
|
|
165
|
+
offset
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
async createDevice(data) {
|
|
169
|
+
const id = generateId();
|
|
170
|
+
const now = /* @__PURE__ */ new Date();
|
|
171
|
+
const deviceData = {
|
|
172
|
+
id,
|
|
173
|
+
enrollmentId: data.enrollmentId,
|
|
174
|
+
externalId: data.externalId ?? null,
|
|
175
|
+
status: "pending",
|
|
176
|
+
model: data.model ?? null,
|
|
177
|
+
manufacturer: data.manufacturer ?? null,
|
|
178
|
+
osVersion: data.osVersion ?? null,
|
|
179
|
+
serialNumber: data.serialNumber ?? null,
|
|
180
|
+
imei: data.imei ?? null,
|
|
181
|
+
macAddress: data.macAddress ?? null,
|
|
182
|
+
androidId: data.androidId ?? null,
|
|
183
|
+
policyId: data.policyId ?? null,
|
|
184
|
+
tags: data.tags ?? null,
|
|
185
|
+
metadata: data.metadata ?? null,
|
|
186
|
+
createdAt: now,
|
|
187
|
+
updatedAt: now
|
|
188
|
+
};
|
|
189
|
+
await db.insert(devices).values(deviceData);
|
|
190
|
+
return this.findDevice(id);
|
|
191
|
+
},
|
|
192
|
+
async updateDevice(id, data) {
|
|
193
|
+
const updateData = {
|
|
194
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
195
|
+
};
|
|
196
|
+
if (data.externalId !== void 0) updateData.externalId = data.externalId;
|
|
197
|
+
if (data.status !== void 0) updateData.status = data.status;
|
|
198
|
+
if (data.policyId !== void 0) updateData.policyId = data.policyId;
|
|
199
|
+
if (data.model !== void 0) updateData.model = data.model;
|
|
200
|
+
if (data.manufacturer !== void 0)
|
|
201
|
+
updateData.manufacturer = data.manufacturer;
|
|
202
|
+
if (data.osVersion !== void 0) updateData.osVersion = data.osVersion;
|
|
203
|
+
if (data.batteryLevel !== void 0)
|
|
204
|
+
updateData.batteryLevel = data.batteryLevel;
|
|
205
|
+
if (data.storageUsed !== void 0)
|
|
206
|
+
updateData.storageUsed = data.storageUsed;
|
|
207
|
+
if (data.storageTotal !== void 0)
|
|
208
|
+
updateData.storageTotal = data.storageTotal;
|
|
209
|
+
if (data.lastHeartbeat !== void 0)
|
|
210
|
+
updateData.lastHeartbeat = data.lastHeartbeat;
|
|
211
|
+
if (data.lastSync !== void 0) updateData.lastSync = data.lastSync;
|
|
212
|
+
if (data.installedApps !== void 0)
|
|
213
|
+
updateData.installedApps = data.installedApps;
|
|
214
|
+
if (data.tags !== void 0) updateData.tags = data.tags;
|
|
215
|
+
if (data.metadata !== void 0) updateData.metadata = data.metadata;
|
|
216
|
+
if (data.location) {
|
|
217
|
+
updateData.latitude = data.location.latitude.toString();
|
|
218
|
+
updateData.longitude = data.location.longitude.toString();
|
|
219
|
+
updateData.locationTimestamp = data.location.timestamp;
|
|
220
|
+
}
|
|
221
|
+
await db.update(devices).set(updateData).where(eq(devices.id, id));
|
|
222
|
+
return this.findDevice(id);
|
|
223
|
+
},
|
|
224
|
+
async deleteDevice(id) {
|
|
225
|
+
await db.delete(devices).where(eq(devices.id, id));
|
|
226
|
+
},
|
|
227
|
+
async countDevices(filter) {
|
|
228
|
+
const result = await this.listDevices({ ...filter, limit: 0 });
|
|
229
|
+
return result.total;
|
|
230
|
+
},
|
|
231
|
+
// ============================================
|
|
232
|
+
// Policy Methods
|
|
233
|
+
// ============================================
|
|
234
|
+
async findPolicy(id) {
|
|
235
|
+
const result = await db.select().from(policies).where(eq(policies.id, id)).limit(1);
|
|
236
|
+
return result[0] ? toPolicy(result[0]) : null;
|
|
237
|
+
},
|
|
238
|
+
async findDefaultPolicy() {
|
|
239
|
+
const result = await db.select().from(policies).where(eq(policies.isDefault, true)).limit(1);
|
|
240
|
+
return result[0] ? toPolicy(result[0]) : null;
|
|
241
|
+
},
|
|
242
|
+
async listPolicies() {
|
|
243
|
+
const result = await db.select().from(policies).orderBy(desc(policies.createdAt));
|
|
244
|
+
return result.map(toPolicy);
|
|
245
|
+
},
|
|
246
|
+
async createPolicy(data) {
|
|
247
|
+
const id = generateId();
|
|
248
|
+
const now = /* @__PURE__ */ new Date();
|
|
249
|
+
const policyData = {
|
|
250
|
+
id,
|
|
251
|
+
name: data.name,
|
|
252
|
+
description: data.description ?? null,
|
|
253
|
+
isDefault: data.isDefault ?? false,
|
|
254
|
+
settings: data.settings,
|
|
255
|
+
createdAt: now,
|
|
256
|
+
updatedAt: now
|
|
257
|
+
};
|
|
258
|
+
await db.insert(policies).values(policyData);
|
|
259
|
+
return this.findPolicy(id);
|
|
260
|
+
},
|
|
261
|
+
async updatePolicy(id, data) {
|
|
262
|
+
const updateData = {
|
|
263
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
264
|
+
};
|
|
265
|
+
if (data.name !== void 0) updateData.name = data.name;
|
|
266
|
+
if (data.description !== void 0)
|
|
267
|
+
updateData.description = data.description;
|
|
268
|
+
if (data.isDefault !== void 0) updateData.isDefault = data.isDefault;
|
|
269
|
+
if (data.settings !== void 0) updateData.settings = data.settings;
|
|
270
|
+
await db.update(policies).set(updateData).where(eq(policies.id, id));
|
|
271
|
+
return this.findPolicy(id);
|
|
272
|
+
},
|
|
273
|
+
async deletePolicy(id) {
|
|
274
|
+
await db.delete(policies).where(eq(policies.id, id));
|
|
275
|
+
},
|
|
276
|
+
// ============================================
|
|
277
|
+
// Application Methods
|
|
278
|
+
// ============================================
|
|
279
|
+
async findApplication(id) {
|
|
280
|
+
const result = await db.select().from(applications).where(eq(applications.id, id)).limit(1);
|
|
281
|
+
return result[0] ? toApplication(result[0]) : null;
|
|
282
|
+
},
|
|
283
|
+
async findApplicationByPackage(packageName, version) {
|
|
284
|
+
let query = db.select().from(applications).where(eq(applications.packageName, packageName));
|
|
285
|
+
if (version) {
|
|
286
|
+
query = query.where(eq(applications.version, version));
|
|
287
|
+
}
|
|
288
|
+
const result = await query.orderBy(desc(applications.versionCode)).limit(1);
|
|
289
|
+
return result[0] ? toApplication(result[0]) : null;
|
|
290
|
+
},
|
|
291
|
+
async listApplications(activeOnly) {
|
|
292
|
+
let query = db.select().from(applications);
|
|
293
|
+
if (activeOnly) {
|
|
294
|
+
query = query.where(eq(applications.isActive, true));
|
|
295
|
+
}
|
|
296
|
+
const result = await query.orderBy(desc(applications.createdAt));
|
|
297
|
+
return result.map(toApplication);
|
|
298
|
+
},
|
|
299
|
+
async createApplication(data) {
|
|
300
|
+
const id = generateId();
|
|
301
|
+
const now = /* @__PURE__ */ new Date();
|
|
302
|
+
const appData = {
|
|
303
|
+
id,
|
|
304
|
+
name: data.name,
|
|
305
|
+
packageName: data.packageName,
|
|
306
|
+
version: data.version,
|
|
307
|
+
versionCode: data.versionCode,
|
|
308
|
+
url: data.url,
|
|
309
|
+
hash: data.hash ?? null,
|
|
310
|
+
size: data.size ?? null,
|
|
311
|
+
minSdkVersion: data.minSdkVersion ?? null,
|
|
312
|
+
showIcon: data.showIcon ?? true,
|
|
313
|
+
runAfterInstall: data.runAfterInstall ?? false,
|
|
314
|
+
runAtBoot: data.runAtBoot ?? false,
|
|
315
|
+
isSystem: data.isSystem ?? false,
|
|
316
|
+
isActive: true,
|
|
317
|
+
metadata: data.metadata ?? null,
|
|
318
|
+
createdAt: now,
|
|
319
|
+
updatedAt: now
|
|
320
|
+
};
|
|
321
|
+
await db.insert(applications).values(appData);
|
|
322
|
+
return this.findApplication(id);
|
|
323
|
+
},
|
|
324
|
+
async updateApplication(id, data) {
|
|
325
|
+
const updateData = {
|
|
326
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
327
|
+
};
|
|
328
|
+
if (data.name !== void 0) updateData.name = data.name;
|
|
329
|
+
if (data.version !== void 0) updateData.version = data.version;
|
|
330
|
+
if (data.versionCode !== void 0)
|
|
331
|
+
updateData.versionCode = data.versionCode;
|
|
332
|
+
if (data.url !== void 0) updateData.url = data.url;
|
|
333
|
+
if (data.hash !== void 0) updateData.hash = data.hash;
|
|
334
|
+
if (data.size !== void 0) updateData.size = data.size;
|
|
335
|
+
if (data.minSdkVersion !== void 0)
|
|
336
|
+
updateData.minSdkVersion = data.minSdkVersion;
|
|
337
|
+
if (data.showIcon !== void 0) updateData.showIcon = data.showIcon;
|
|
338
|
+
if (data.runAfterInstall !== void 0)
|
|
339
|
+
updateData.runAfterInstall = data.runAfterInstall;
|
|
340
|
+
if (data.runAtBoot !== void 0) updateData.runAtBoot = data.runAtBoot;
|
|
341
|
+
if (data.isActive !== void 0) updateData.isActive = data.isActive;
|
|
342
|
+
if (data.metadata !== void 0) updateData.metadata = data.metadata;
|
|
343
|
+
await db.update(applications).set(updateData).where(eq(applications.id, id));
|
|
344
|
+
return this.findApplication(id);
|
|
345
|
+
},
|
|
346
|
+
async deleteApplication(id) {
|
|
347
|
+
await db.delete(applications).where(eq(applications.id, id));
|
|
348
|
+
},
|
|
349
|
+
// ============================================
|
|
350
|
+
// Command Methods
|
|
351
|
+
// ============================================
|
|
352
|
+
async findCommand(id) {
|
|
353
|
+
const result = await db.select().from(commands).where(eq(commands.id, id)).limit(1);
|
|
354
|
+
return result[0] ? toCommand(result[0]) : null;
|
|
355
|
+
},
|
|
356
|
+
async listCommands(filter) {
|
|
357
|
+
let query = db.select().from(commands);
|
|
358
|
+
const conditions = [];
|
|
359
|
+
if (filter?.deviceId) {
|
|
360
|
+
conditions.push(eq(commands.deviceId, filter.deviceId));
|
|
361
|
+
}
|
|
362
|
+
if (filter?.status) {
|
|
363
|
+
if (Array.isArray(filter.status)) {
|
|
364
|
+
conditions.push(inArray(commands.status, filter.status));
|
|
365
|
+
} else {
|
|
366
|
+
conditions.push(eq(commands.status, filter.status));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (filter?.type) {
|
|
370
|
+
if (Array.isArray(filter.type)) {
|
|
371
|
+
conditions.push(inArray(commands.type, filter.type));
|
|
372
|
+
} else {
|
|
373
|
+
conditions.push(eq(commands.type, filter.type));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (conditions.length > 0) {
|
|
377
|
+
query = query.where(and(...conditions));
|
|
378
|
+
}
|
|
379
|
+
const limit = filter?.limit ?? 100;
|
|
380
|
+
const offset = filter?.offset ?? 0;
|
|
381
|
+
const result = await query.orderBy(desc(commands.createdAt)).limit(limit).offset(offset);
|
|
382
|
+
return result.map(toCommand);
|
|
383
|
+
},
|
|
384
|
+
async createCommand(data) {
|
|
385
|
+
const id = generateId();
|
|
386
|
+
const now = /* @__PURE__ */ new Date();
|
|
387
|
+
const commandData = {
|
|
388
|
+
id,
|
|
389
|
+
deviceId: data.deviceId,
|
|
390
|
+
type: data.type,
|
|
391
|
+
payload: data.payload ?? null,
|
|
392
|
+
status: "pending",
|
|
393
|
+
createdAt: now
|
|
394
|
+
};
|
|
395
|
+
await db.insert(commands).values(commandData);
|
|
396
|
+
return this.findCommand(id);
|
|
397
|
+
},
|
|
398
|
+
async updateCommand(id, data) {
|
|
399
|
+
const updateData = {};
|
|
400
|
+
if (data.status !== void 0) updateData.status = data.status;
|
|
401
|
+
if (data.result !== void 0) updateData.result = data.result;
|
|
402
|
+
if (data.error !== void 0) updateData.error = data.error;
|
|
403
|
+
if (data.sentAt !== void 0) updateData.sentAt = data.sentAt;
|
|
404
|
+
if (data.acknowledgedAt !== void 0)
|
|
405
|
+
updateData.acknowledgedAt = data.acknowledgedAt;
|
|
406
|
+
if (data.completedAt !== void 0)
|
|
407
|
+
updateData.completedAt = data.completedAt;
|
|
408
|
+
await db.update(commands).set(updateData).where(eq(commands.id, id));
|
|
409
|
+
return this.findCommand(id);
|
|
410
|
+
},
|
|
411
|
+
async getPendingCommands(deviceId) {
|
|
412
|
+
return this.listCommands({
|
|
413
|
+
deviceId,
|
|
414
|
+
status: ["pending", "sent"]
|
|
415
|
+
});
|
|
416
|
+
},
|
|
417
|
+
// ============================================
|
|
418
|
+
// Event Methods
|
|
419
|
+
// ============================================
|
|
420
|
+
async createEvent(data) {
|
|
421
|
+
const id = generateId();
|
|
422
|
+
const now = /* @__PURE__ */ new Date();
|
|
423
|
+
const eventData = {
|
|
424
|
+
id,
|
|
425
|
+
deviceId: data.deviceId,
|
|
426
|
+
type: data.type,
|
|
427
|
+
payload: data.payload,
|
|
428
|
+
createdAt: now
|
|
429
|
+
};
|
|
430
|
+
await db.insert(events).values(eventData);
|
|
431
|
+
return {
|
|
432
|
+
...eventData,
|
|
433
|
+
createdAt: now
|
|
434
|
+
};
|
|
435
|
+
},
|
|
436
|
+
async listEvents(filter) {
|
|
437
|
+
let query = db.select().from(events);
|
|
438
|
+
const conditions = [];
|
|
439
|
+
if (filter?.deviceId) {
|
|
440
|
+
conditions.push(eq(events.deviceId, filter.deviceId));
|
|
441
|
+
}
|
|
442
|
+
if (filter?.type) {
|
|
443
|
+
if (Array.isArray(filter.type)) {
|
|
444
|
+
conditions.push(inArray(events.type, filter.type));
|
|
445
|
+
} else {
|
|
446
|
+
conditions.push(eq(events.type, filter.type));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (conditions.length > 0) {
|
|
450
|
+
query = query.where(and(...conditions));
|
|
451
|
+
}
|
|
452
|
+
const limit = filter?.limit ?? 100;
|
|
453
|
+
const offset = filter?.offset ?? 0;
|
|
454
|
+
const result = await query.orderBy(desc(events.createdAt)).limit(limit).offset(offset);
|
|
455
|
+
return result.map(toEvent);
|
|
456
|
+
},
|
|
457
|
+
// ============================================
|
|
458
|
+
// Group Methods
|
|
459
|
+
// ============================================
|
|
460
|
+
async findGroup(id) {
|
|
461
|
+
const result = await db.select().from(groups).where(eq(groups.id, id)).limit(1);
|
|
462
|
+
return result[0] ? toGroup(result[0]) : null;
|
|
463
|
+
},
|
|
464
|
+
async listGroups() {
|
|
465
|
+
const result = await db.select().from(groups).orderBy(groups.name);
|
|
466
|
+
return result.map(toGroup);
|
|
467
|
+
},
|
|
468
|
+
async createGroup(data) {
|
|
469
|
+
const id = generateId();
|
|
470
|
+
const now = /* @__PURE__ */ new Date();
|
|
471
|
+
const groupData = {
|
|
472
|
+
id,
|
|
473
|
+
name: data.name,
|
|
474
|
+
description: data.description ?? null,
|
|
475
|
+
policyId: data.policyId ?? null,
|
|
476
|
+
parentId: data.parentId ?? null,
|
|
477
|
+
metadata: data.metadata ?? null,
|
|
478
|
+
createdAt: now,
|
|
479
|
+
updatedAt: now
|
|
480
|
+
};
|
|
481
|
+
await db.insert(groups).values(groupData);
|
|
482
|
+
return this.findGroup(id);
|
|
483
|
+
},
|
|
484
|
+
async updateGroup(id, data) {
|
|
485
|
+
const updateData = {
|
|
486
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
487
|
+
};
|
|
488
|
+
if (data.name !== void 0) updateData.name = data.name;
|
|
489
|
+
if (data.description !== void 0)
|
|
490
|
+
updateData.description = data.description;
|
|
491
|
+
if (data.policyId !== void 0) updateData.policyId = data.policyId;
|
|
492
|
+
if (data.parentId !== void 0) updateData.parentId = data.parentId;
|
|
493
|
+
if (data.metadata !== void 0) updateData.metadata = data.metadata;
|
|
494
|
+
await db.update(groups).set(updateData).where(eq(groups.id, id));
|
|
495
|
+
return this.findGroup(id);
|
|
496
|
+
},
|
|
497
|
+
async deleteGroup(id) {
|
|
498
|
+
await db.delete(groups).where(eq(groups.id, id));
|
|
499
|
+
},
|
|
500
|
+
async listDevicesInGroup(groupId) {
|
|
501
|
+
const result = await db.select({ device: devices }).from(deviceGroups).innerJoin(devices, eq(deviceGroups.deviceId, devices.id)).where(eq(deviceGroups.groupId, groupId));
|
|
502
|
+
return result.map(
|
|
503
|
+
(r) => toDevice(r.device)
|
|
504
|
+
);
|
|
505
|
+
},
|
|
506
|
+
async addDeviceToGroup(deviceId, groupId) {
|
|
507
|
+
await db.insert(deviceGroups).values({
|
|
508
|
+
deviceId,
|
|
509
|
+
groupId,
|
|
510
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
511
|
+
});
|
|
512
|
+
},
|
|
513
|
+
async removeDeviceFromGroup(deviceId, groupId) {
|
|
514
|
+
await db.delete(deviceGroups).where(
|
|
515
|
+
and(
|
|
516
|
+
eq(deviceGroups.deviceId, deviceId),
|
|
517
|
+
eq(deviceGroups.groupId, groupId)
|
|
518
|
+
)
|
|
519
|
+
);
|
|
520
|
+
},
|
|
521
|
+
async getDeviceGroups(deviceId) {
|
|
522
|
+
const result = await db.select({ group: groups }).from(deviceGroups).innerJoin(groups, eq(deviceGroups.groupId, groups.id)).where(eq(deviceGroups.deviceId, deviceId));
|
|
523
|
+
return result.map(
|
|
524
|
+
(r) => toGroup(r.group)
|
|
525
|
+
);
|
|
526
|
+
},
|
|
527
|
+
// ============================================
|
|
528
|
+
// Push Token Methods
|
|
529
|
+
// ============================================
|
|
530
|
+
async findPushToken(deviceId, provider) {
|
|
531
|
+
const result = await db.select().from(pushTokens).where(
|
|
532
|
+
and(
|
|
533
|
+
eq(pushTokens.deviceId, deviceId),
|
|
534
|
+
eq(pushTokens.provider, provider)
|
|
535
|
+
)
|
|
536
|
+
).limit(1);
|
|
537
|
+
return result[0] ? toPushToken(result[0]) : null;
|
|
538
|
+
},
|
|
539
|
+
async upsertPushToken(data) {
|
|
540
|
+
const existing = await this.findPushToken(data.deviceId, data.provider);
|
|
541
|
+
const now = /* @__PURE__ */ new Date();
|
|
542
|
+
if (existing) {
|
|
543
|
+
await db.update(pushTokens).set({
|
|
544
|
+
token: data.token,
|
|
545
|
+
isActive: true,
|
|
546
|
+
updatedAt: now
|
|
547
|
+
}).where(eq(pushTokens.id, existing.id));
|
|
548
|
+
return this.findPushToken(data.deviceId, data.provider);
|
|
549
|
+
}
|
|
550
|
+
const id = generateId();
|
|
551
|
+
await db.insert(pushTokens).values({
|
|
552
|
+
id,
|
|
553
|
+
deviceId: data.deviceId,
|
|
554
|
+
provider: data.provider,
|
|
555
|
+
token: data.token,
|
|
556
|
+
isActive: true,
|
|
557
|
+
createdAt: now,
|
|
558
|
+
updatedAt: now
|
|
559
|
+
});
|
|
560
|
+
return this.findPushToken(data.deviceId, data.provider);
|
|
561
|
+
},
|
|
562
|
+
async deletePushToken(deviceId, provider) {
|
|
563
|
+
if (provider) {
|
|
564
|
+
await db.delete(pushTokens).where(
|
|
565
|
+
and(
|
|
566
|
+
eq(pushTokens.deviceId, deviceId),
|
|
567
|
+
eq(pushTokens.provider, provider)
|
|
568
|
+
)
|
|
569
|
+
);
|
|
570
|
+
} else {
|
|
571
|
+
await db.delete(pushTokens).where(eq(pushTokens.deviceId, deviceId));
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
// ============================================
|
|
575
|
+
// Transaction Support
|
|
576
|
+
// ============================================
|
|
577
|
+
async transaction(fn) {
|
|
578
|
+
return db.transaction(async () => {
|
|
579
|
+
return fn();
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
export {
|
|
585
|
+
DEFAULT_TABLE_PREFIX,
|
|
586
|
+
drizzleAdapter
|
|
587
|
+
};
|
|
588
|
+
//# sourceMappingURL=index.js.map
|