@tomorrowos/sdk 0.8.3 → 0.8.5
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/REPLIT_SETUP.md +1 -1
- package/dist/store/memory-store.d.ts +1 -1
- package/dist/store/memory-store.d.ts.map +1 -1
- package/dist/store/memory-store.js +2 -3
- package/dist/store/postgres-store.d.ts +3 -2
- package/dist/store/postgres-store.d.ts.map +1 -1
- package/dist/store/postgres-store.js +89 -17
- package/dist/store/sqlite-store.d.ts +3 -2
- package/dist/store/sqlite-store.d.ts.map +1 -1
- package/dist/store/sqlite-store.js +104 -17
- package/dist/store/types.d.ts +4 -3
- package/dist/store/types.d.ts.map +1 -1
- package/dist/tomorrowos.d.ts +4 -2
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +76 -41
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/public/index.html +17 -0
- package/templates/cms-starter/public/methods.js +60 -0
package/REPLIT_SETUP.md
CHANGED
|
@@ -29,7 +29,7 @@ export declare class MemoryStore implements TomorrowOSStore {
|
|
|
29
29
|
setPlaylist(record: StoredPlaylist): Promise<void>;
|
|
30
30
|
isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
|
|
31
31
|
getUploadedAsset(id: string): Promise<UploadedAssetRecord | undefined>;
|
|
32
|
-
getUploadedAssetBySha256(sha256: string
|
|
32
|
+
getUploadedAssetBySha256(sha256: string): Promise<UploadedAssetRecord | undefined>;
|
|
33
33
|
setUploadedAsset(record: UploadedAssetRecord): Promise<void>;
|
|
34
34
|
deleteUploadedAsset(id: string): Promise<void>;
|
|
35
35
|
getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAE7E,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAItC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IASV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAQpE,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAIpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOjD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUvE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAItE,wBAAwB,CAC5B,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;IAC1E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAE7E,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,iBAAiB,CACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAItC,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IASV,uBAAuB,CAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,oBAAoB,CAAA;KAAE,GAAG,SAAS,CAAC;IAQpE,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAIpC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOjD,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAI1C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI5D,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUvE,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAItE,wBAAwB,CAC5B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IASrC,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,oBAAoB,CACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,wBAAwB,EAAE,CAAC;IAIhC,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,wBAAwB,EAAE,GACtC,OAAO,CAAC,IAAI,CAAC;CASjB"}
|
|
@@ -78,10 +78,9 @@ export class MemoryStore {
|
|
|
78
78
|
async getUploadedAsset(id) {
|
|
79
79
|
return this.uploadedAssets.get(id);
|
|
80
80
|
}
|
|
81
|
-
async getUploadedAssetBySha256(sha256
|
|
81
|
+
async getUploadedAssetBySha256(sha256) {
|
|
82
82
|
for (const asset of this.uploadedAssets.values()) {
|
|
83
|
-
if (asset.sha256 === sha256
|
|
84
|
-
(!storageProvider || asset.storageProvider === storageProvider)) {
|
|
83
|
+
if (asset.sha256 === sha256) {
|
|
85
84
|
return asset;
|
|
86
85
|
}
|
|
87
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type PoolConfig } from "pg";
|
|
2
|
-
import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore, UploadedAssetRecord
|
|
2
|
+
import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore, UploadedAssetRecord } from "./types.js";
|
|
3
3
|
export interface PostgresStoreOptions {
|
|
4
4
|
connectionString: string;
|
|
5
5
|
ssl?: PoolConfig["ssl"];
|
|
@@ -13,6 +13,7 @@ export declare class PostgresStore implements TomorrowOSMigratableStore {
|
|
|
13
13
|
private ready?;
|
|
14
14
|
constructor(options: PostgresStoreOptions | string);
|
|
15
15
|
init(): Promise<void>;
|
|
16
|
+
private migrateDropUploadedAssetStorageProvider;
|
|
16
17
|
private migrateDropPlaylistVersionColumns;
|
|
17
18
|
close(): Promise<void>;
|
|
18
19
|
private ensureReady;
|
|
@@ -36,7 +37,7 @@ export declare class PostgresStore implements TomorrowOSMigratableStore {
|
|
|
36
37
|
setPlaylist(record: StoredPlaylist): Promise<void>;
|
|
37
38
|
isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
|
|
38
39
|
getUploadedAsset(id: string): Promise<UploadedAssetRecord | undefined>;
|
|
39
|
-
getUploadedAssetBySha256(sha256: string
|
|
40
|
+
getUploadedAssetBySha256(sha256: string): Promise<UploadedAssetRecord | undefined>;
|
|
40
41
|
setUploadedAsset(record: UploadedAssetRecord): Promise<void>;
|
|
41
42
|
deleteUploadedAsset(id: string): Promise<void>;
|
|
42
43
|
listUploadedAssets(): Promise<UploadedAssetRecord[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EACzB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/store/postgres-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -95,8 +95,7 @@ export class PostgresStore {
|
|
|
95
95
|
|
|
96
96
|
CREATE TABLE IF NOT EXISTS uploaded_assets (
|
|
97
97
|
id TEXT PRIMARY KEY,
|
|
98
|
-
sha256 TEXT NOT NULL,
|
|
99
|
-
storage_provider TEXT NOT NULL,
|
|
98
|
+
sha256 TEXT NOT NULL UNIQUE,
|
|
100
99
|
storage_key TEXT NOT NULL,
|
|
101
100
|
url TEXT NOT NULL,
|
|
102
101
|
original_filename TEXT,
|
|
@@ -104,8 +103,7 @@ export class PostgresStore {
|
|
|
104
103
|
resource_type TEXT,
|
|
105
104
|
bytes BIGINT,
|
|
106
105
|
created_at TEXT NOT NULL,
|
|
107
|
-
updated_at TEXT NOT NULL
|
|
108
|
-
UNIQUE (storage_provider, sha256)
|
|
106
|
+
updated_at TEXT NOT NULL
|
|
109
107
|
);
|
|
110
108
|
|
|
111
109
|
CREATE INDEX IF NOT EXISTS uploaded_assets_sha256_idx
|
|
@@ -120,8 +118,79 @@ export class PostgresStore {
|
|
|
120
118
|
|
|
121
119
|
ALTER TABLE paired_devices
|
|
122
120
|
ADD COLUMN IF NOT EXISTS system_version TEXT;
|
|
121
|
+
|
|
122
|
+
ALTER TABLE paired_devices
|
|
123
|
+
ADD COLUMN IF NOT EXISTS last_screenshot_asset_id TEXT;
|
|
124
|
+
|
|
125
|
+
ALTER TABLE paired_devices
|
|
126
|
+
ADD COLUMN IF NOT EXISTS last_screenshot_captured_at TEXT;
|
|
123
127
|
`);
|
|
124
128
|
await this.migrateDropPlaylistVersionColumns();
|
|
129
|
+
await this.migrateDropUploadedAssetStorageProvider();
|
|
130
|
+
}
|
|
131
|
+
async migrateDropUploadedAssetStorageProvider() {
|
|
132
|
+
const applied = await this.pool.query(`
|
|
133
|
+
SELECT 1
|
|
134
|
+
FROM schema_migrations
|
|
135
|
+
WHERE name = 'drop_uploaded_asset_storage_provider'
|
|
136
|
+
LIMIT 1
|
|
137
|
+
`);
|
|
138
|
+
if (applied.rows.length > 0)
|
|
139
|
+
return;
|
|
140
|
+
const columnExists = await this.pool.query(`
|
|
141
|
+
SELECT EXISTS (
|
|
142
|
+
SELECT 1
|
|
143
|
+
FROM information_schema.columns
|
|
144
|
+
WHERE table_name = 'uploaded_assets'
|
|
145
|
+
AND column_name = 'storage_provider'
|
|
146
|
+
) AS exists
|
|
147
|
+
`);
|
|
148
|
+
if (!columnExists.rows[0]?.exists) {
|
|
149
|
+
await this.pool.query(`
|
|
150
|
+
INSERT INTO schema_migrations (id, name)
|
|
151
|
+
VALUES (2, 'drop_uploaded_asset_storage_provider')
|
|
152
|
+
ON CONFLICT (id) DO NOTHING
|
|
153
|
+
`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
await this.pool.query(`
|
|
157
|
+
DELETE FROM uploaded_assets a
|
|
158
|
+
USING uploaded_assets b
|
|
159
|
+
WHERE a.sha256 = b.sha256
|
|
160
|
+
AND (
|
|
161
|
+
CASE WHEN a.url LIKE 'http%' THEN 0 ELSE 1 END,
|
|
162
|
+
a.updated_at
|
|
163
|
+
) > (
|
|
164
|
+
CASE WHEN b.url LIKE 'http%' THEN 0 ELSE 1 END,
|
|
165
|
+
b.updated_at
|
|
166
|
+
)
|
|
167
|
+
`);
|
|
168
|
+
await this.pool.query(`
|
|
169
|
+
ALTER TABLE uploaded_assets
|
|
170
|
+
DROP CONSTRAINT IF EXISTS uploaded_assets_storage_provider_sha256_key
|
|
171
|
+
`);
|
|
172
|
+
await this.pool.query(`
|
|
173
|
+
ALTER TABLE uploaded_assets
|
|
174
|
+
DROP COLUMN IF EXISTS storage_provider
|
|
175
|
+
`);
|
|
176
|
+
await this.pool.query(`
|
|
177
|
+
DO $$
|
|
178
|
+
BEGIN
|
|
179
|
+
IF NOT EXISTS (
|
|
180
|
+
SELECT 1
|
|
181
|
+
FROM pg_constraint
|
|
182
|
+
WHERE conname = 'uploaded_assets_sha256_key'
|
|
183
|
+
) THEN
|
|
184
|
+
ALTER TABLE uploaded_assets
|
|
185
|
+
ADD CONSTRAINT uploaded_assets_sha256_key UNIQUE (sha256);
|
|
186
|
+
END IF;
|
|
187
|
+
END $$;
|
|
188
|
+
`);
|
|
189
|
+
await this.pool.query(`
|
|
190
|
+
INSERT INTO schema_migrations (id, name)
|
|
191
|
+
VALUES (2, 'drop_uploaded_asset_storage_provider')
|
|
192
|
+
ON CONFLICT (id) DO NOTHING
|
|
193
|
+
`);
|
|
125
194
|
}
|
|
126
195
|
async migrateDropPlaylistVersionColumns() {
|
|
127
196
|
const applied = await this.pool.query(`
|
|
@@ -286,9 +355,11 @@ export class PostgresStore {
|
|
|
286
355
|
last_boot_at,
|
|
287
356
|
last_online_at,
|
|
288
357
|
last_offline_at,
|
|
289
|
-
last_policy_push_at
|
|
358
|
+
last_policy_push_at,
|
|
359
|
+
last_screenshot_asset_id,
|
|
360
|
+
last_screenshot_captured_at
|
|
290
361
|
)
|
|
291
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
362
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
|
292
363
|
ON CONFLICT (device_id) DO UPDATE SET
|
|
293
364
|
pairing_token = EXCLUDED.pairing_token,
|
|
294
365
|
paired_at = EXCLUDED.paired_at,
|
|
@@ -300,7 +371,9 @@ export class PostgresStore {
|
|
|
300
371
|
last_boot_at = EXCLUDED.last_boot_at,
|
|
301
372
|
last_online_at = EXCLUDED.last_online_at,
|
|
302
373
|
last_offline_at = EXCLUDED.last_offline_at,
|
|
303
|
-
last_policy_push_at = EXCLUDED.last_policy_push_at
|
|
374
|
+
last_policy_push_at = EXCLUDED.last_policy_push_at,
|
|
375
|
+
last_screenshot_asset_id = EXCLUDED.last_screenshot_asset_id,
|
|
376
|
+
last_screenshot_captured_at = EXCLUDED.last_screenshot_captured_at
|
|
304
377
|
`, [
|
|
305
378
|
deviceId,
|
|
306
379
|
record.pairingToken,
|
|
@@ -313,7 +386,9 @@ export class PostgresStore {
|
|
|
313
386
|
record.lastBootAt ?? null,
|
|
314
387
|
record.lastOnlineAt ?? null,
|
|
315
388
|
record.lastOfflineAt ?? null,
|
|
316
|
-
record.lastPolicyPushAt ?? null
|
|
389
|
+
record.lastPolicyPushAt ?? null,
|
|
390
|
+
record.lastScreenshotAssetId ?? null,
|
|
391
|
+
record.lastScreenshotCapturedAt ?? null
|
|
317
392
|
]);
|
|
318
393
|
}
|
|
319
394
|
async getPairedDevice(deviceId) {
|
|
@@ -418,15 +493,14 @@ export class PostgresStore {
|
|
|
418
493
|
const row = result.rows[0];
|
|
419
494
|
return row ? this.mapUploadedAssetRow(row) : undefined;
|
|
420
495
|
}
|
|
421
|
-
async getUploadedAssetBySha256(sha256
|
|
496
|
+
async getUploadedAssetBySha256(sha256) {
|
|
422
497
|
await this.ensureReady();
|
|
423
498
|
const result = await this.pool.query(`
|
|
424
499
|
SELECT *
|
|
425
500
|
FROM uploaded_assets
|
|
426
501
|
WHERE sha256 = $1
|
|
427
|
-
AND ($2::text IS NULL OR storage_provider = $2)
|
|
428
502
|
LIMIT 1
|
|
429
|
-
`, [sha256
|
|
503
|
+
`, [sha256]);
|
|
430
504
|
const row = result.rows[0];
|
|
431
505
|
return row ? this.mapUploadedAssetRow(row) : undefined;
|
|
432
506
|
}
|
|
@@ -436,7 +510,6 @@ export class PostgresStore {
|
|
|
436
510
|
INSERT INTO uploaded_assets (
|
|
437
511
|
id,
|
|
438
512
|
sha256,
|
|
439
|
-
storage_provider,
|
|
440
513
|
storage_key,
|
|
441
514
|
url,
|
|
442
515
|
original_filename,
|
|
@@ -446,10 +519,9 @@ export class PostgresStore {
|
|
|
446
519
|
created_at,
|
|
447
520
|
updated_at
|
|
448
521
|
)
|
|
449
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
|
522
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
450
523
|
ON CONFLICT (id) DO UPDATE SET
|
|
451
524
|
sha256 = EXCLUDED.sha256,
|
|
452
|
-
storage_provider = EXCLUDED.storage_provider,
|
|
453
525
|
storage_key = EXCLUDED.storage_key,
|
|
454
526
|
url = EXCLUDED.url,
|
|
455
527
|
original_filename = EXCLUDED.original_filename,
|
|
@@ -460,7 +532,6 @@ export class PostgresStore {
|
|
|
460
532
|
`, [
|
|
461
533
|
record.id,
|
|
462
534
|
record.sha256,
|
|
463
|
-
record.storageProvider,
|
|
464
535
|
record.storageKey,
|
|
465
536
|
record.url,
|
|
466
537
|
record.originalFilename ?? null,
|
|
@@ -559,7 +630,9 @@ export class PostgresStore {
|
|
|
559
630
|
lastBootAt: optionalString(row.last_boot_at),
|
|
560
631
|
lastOnlineAt: optionalString(row.last_online_at),
|
|
561
632
|
lastOfflineAt: optionalString(row.last_offline_at),
|
|
562
|
-
lastPolicyPushAt: optionalString(row.last_policy_push_at)
|
|
633
|
+
lastPolicyPushAt: optionalString(row.last_policy_push_at),
|
|
634
|
+
lastScreenshotAssetId: optionalString(row.last_screenshot_asset_id),
|
|
635
|
+
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at)
|
|
563
636
|
};
|
|
564
637
|
}
|
|
565
638
|
mapPlaylistRow(row) {
|
|
@@ -577,7 +650,6 @@ export class PostgresStore {
|
|
|
577
650
|
return {
|
|
578
651
|
id: row.id,
|
|
579
652
|
sha256: row.sha256,
|
|
580
|
-
storageProvider: row.storage_provider,
|
|
581
653
|
storageKey: row.storage_key,
|
|
582
654
|
url: row.url,
|
|
583
655
|
originalFilename: optionalString(row.original_filename),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore, UploadedAssetRecord
|
|
1
|
+
import type { DevicePlaylistAssignment, DeviceRegistryEntry, DeviceRegistryRecord, PairedDeviceEntry, PairedDeviceRecord, PendingCodeEntry, PendingCodeRecord, StoredPlaylist, TomorrowOSMigratableStore, UploadedAssetRecord } from "./types.js";
|
|
2
2
|
interface SQLiteStoreOptions {
|
|
3
3
|
databasePath?: string;
|
|
4
4
|
}
|
|
@@ -11,6 +11,7 @@ export declare class SQLiteStore implements TomorrowOSMigratableStore {
|
|
|
11
11
|
private readonly db;
|
|
12
12
|
constructor(options?: SQLiteStoreOptions | string);
|
|
13
13
|
init(): void;
|
|
14
|
+
private migrateDropUploadedAssetStorageProvider;
|
|
14
15
|
private tableHasColumn;
|
|
15
16
|
private migrateDropPlaylistVersionColumns;
|
|
16
17
|
private addColumnIfMissing;
|
|
@@ -35,7 +36,7 @@ export declare class SQLiteStore implements TomorrowOSMigratableStore {
|
|
|
35
36
|
setPlaylist(record: StoredPlaylist): Promise<void>;
|
|
36
37
|
isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
|
|
37
38
|
getUploadedAsset(id: string): Promise<UploadedAssetRecord | undefined>;
|
|
38
|
-
getUploadedAssetBySha256(sha256: string
|
|
39
|
+
getUploadedAssetBySha256(sha256: string): Promise<UploadedAssetRecord | undefined>;
|
|
39
40
|
setUploadedAsset(record: UploadedAssetRecord): Promise<void>;
|
|
40
41
|
deleteUploadedAsset(id: string): Promise<void>;
|
|
41
42
|
listUploadedAssets(): Promise<UploadedAssetRecord[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAGjB,cAAc,EACd,yBAAyB,EACzB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -101,8 +101,7 @@ export class SQLiteStore {
|
|
|
101
101
|
|
|
102
102
|
CREATE TABLE IF NOT EXISTS uploaded_assets (
|
|
103
103
|
id TEXT PRIMARY KEY,
|
|
104
|
-
sha256 TEXT NOT NULL,
|
|
105
|
-
storage_provider TEXT NOT NULL,
|
|
104
|
+
sha256 TEXT NOT NULL UNIQUE,
|
|
106
105
|
storage_key TEXT NOT NULL,
|
|
107
106
|
url TEXT NOT NULL,
|
|
108
107
|
original_filename TEXT,
|
|
@@ -110,8 +109,7 @@ export class SQLiteStore {
|
|
|
110
109
|
resource_type TEXT,
|
|
111
110
|
bytes INTEGER,
|
|
112
111
|
created_at TEXT NOT NULL,
|
|
113
|
-
updated_at TEXT NOT NULL
|
|
114
|
-
UNIQUE (storage_provider, sha256)
|
|
112
|
+
updated_at TEXT NOT NULL
|
|
115
113
|
);
|
|
116
114
|
|
|
117
115
|
CREATE INDEX IF NOT EXISTS uploaded_assets_sha256_idx
|
|
@@ -122,7 +120,94 @@ export class SQLiteStore {
|
|
|
122
120
|
`);
|
|
123
121
|
this.addColumnIfMissing("paired_devices", "player_version TEXT");
|
|
124
122
|
this.addColumnIfMissing("paired_devices", "system_version TEXT");
|
|
123
|
+
this.addColumnIfMissing("paired_devices", "last_screenshot_asset_id TEXT");
|
|
124
|
+
this.addColumnIfMissing("paired_devices", "last_screenshot_captured_at TEXT");
|
|
125
125
|
this.migrateDropPlaylistVersionColumns();
|
|
126
|
+
this.migrateDropUploadedAssetStorageProvider();
|
|
127
|
+
}
|
|
128
|
+
migrateDropUploadedAssetStorageProvider() {
|
|
129
|
+
const alreadyApplied = this.db.prepare(`
|
|
130
|
+
SELECT 1
|
|
131
|
+
FROM schema_migrations
|
|
132
|
+
WHERE name = 'drop_uploaded_asset_storage_provider'
|
|
133
|
+
LIMIT 1
|
|
134
|
+
`).get();
|
|
135
|
+
if (alreadyApplied)
|
|
136
|
+
return;
|
|
137
|
+
if (!this.tableHasColumn("uploaded_assets", "storage_provider")) {
|
|
138
|
+
this.db.prepare(`
|
|
139
|
+
INSERT OR IGNORE INTO schema_migrations (id, name)
|
|
140
|
+
VALUES (2, 'drop_uploaded_asset_storage_provider')
|
|
141
|
+
`).run();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.db.exec(`
|
|
145
|
+
CREATE TABLE uploaded_assets_new (
|
|
146
|
+
id TEXT PRIMARY KEY,
|
|
147
|
+
sha256 TEXT NOT NULL UNIQUE,
|
|
148
|
+
storage_key TEXT NOT NULL,
|
|
149
|
+
url TEXT NOT NULL,
|
|
150
|
+
original_filename TEXT,
|
|
151
|
+
mime_type TEXT,
|
|
152
|
+
resource_type TEXT,
|
|
153
|
+
bytes INTEGER,
|
|
154
|
+
created_at TEXT NOT NULL,
|
|
155
|
+
updated_at TEXT NOT NULL
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
INSERT INTO uploaded_assets_new (
|
|
159
|
+
id,
|
|
160
|
+
sha256,
|
|
161
|
+
storage_key,
|
|
162
|
+
url,
|
|
163
|
+
original_filename,
|
|
164
|
+
mime_type,
|
|
165
|
+
resource_type,
|
|
166
|
+
bytes,
|
|
167
|
+
created_at,
|
|
168
|
+
updated_at
|
|
169
|
+
)
|
|
170
|
+
SELECT
|
|
171
|
+
id,
|
|
172
|
+
sha256,
|
|
173
|
+
storage_key,
|
|
174
|
+
url,
|
|
175
|
+
original_filename,
|
|
176
|
+
mime_type,
|
|
177
|
+
resource_type,
|
|
178
|
+
bytes,
|
|
179
|
+
created_at,
|
|
180
|
+
updated_at
|
|
181
|
+
FROM (
|
|
182
|
+
SELECT
|
|
183
|
+
id,
|
|
184
|
+
sha256,
|
|
185
|
+
storage_key,
|
|
186
|
+
url,
|
|
187
|
+
original_filename,
|
|
188
|
+
mime_type,
|
|
189
|
+
resource_type,
|
|
190
|
+
bytes,
|
|
191
|
+
created_at,
|
|
192
|
+
updated_at,
|
|
193
|
+
ROW_NUMBER() OVER (
|
|
194
|
+
PARTITION BY sha256
|
|
195
|
+
ORDER BY CASE WHEN url LIKE 'http%' THEN 0 ELSE 1 END, updated_at DESC
|
|
196
|
+
) AS rn
|
|
197
|
+
FROM uploaded_assets
|
|
198
|
+
)
|
|
199
|
+
WHERE rn = 1;
|
|
200
|
+
|
|
201
|
+
DROP TABLE uploaded_assets;
|
|
202
|
+
ALTER TABLE uploaded_assets_new RENAME TO uploaded_assets;
|
|
203
|
+
|
|
204
|
+
CREATE INDEX IF NOT EXISTS uploaded_assets_sha256_idx
|
|
205
|
+
ON uploaded_assets (sha256);
|
|
206
|
+
`);
|
|
207
|
+
this.db.prepare(`
|
|
208
|
+
INSERT OR IGNORE INTO schema_migrations (id, name)
|
|
209
|
+
VALUES (2, 'drop_uploaded_asset_storage_provider')
|
|
210
|
+
`).run();
|
|
126
211
|
}
|
|
127
212
|
tableHasColumn(table, column) {
|
|
128
213
|
const rows = this.db.pragma(`table_info(${table})`);
|
|
@@ -323,9 +408,11 @@ export class SQLiteStore {
|
|
|
323
408
|
last_boot_at,
|
|
324
409
|
last_online_at,
|
|
325
410
|
last_offline_at,
|
|
326
|
-
last_policy_push_at
|
|
411
|
+
last_policy_push_at,
|
|
412
|
+
last_screenshot_asset_id,
|
|
413
|
+
last_screenshot_captured_at
|
|
327
414
|
)
|
|
328
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
415
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
329
416
|
ON CONFLICT(device_id) DO UPDATE SET
|
|
330
417
|
pairing_token = excluded.pairing_token,
|
|
331
418
|
paired_at = excluded.paired_at,
|
|
@@ -337,8 +424,10 @@ export class SQLiteStore {
|
|
|
337
424
|
last_boot_at = excluded.last_boot_at,
|
|
338
425
|
last_online_at = excluded.last_online_at,
|
|
339
426
|
last_offline_at = excluded.last_offline_at,
|
|
340
|
-
last_policy_push_at = excluded.last_policy_push_at
|
|
341
|
-
|
|
427
|
+
last_policy_push_at = excluded.last_policy_push_at,
|
|
428
|
+
last_screenshot_asset_id = excluded.last_screenshot_asset_id,
|
|
429
|
+
last_screenshot_captured_at = excluded.last_screenshot_captured_at
|
|
430
|
+
`).run(deviceId, record.pairingToken, record.pairedAt, record.deviceName ?? null, record.platform ?? null, record.system ?? null, record.playerVersion ?? null, record.systemVersion ?? null, record.lastBootAt ?? null, record.lastOnlineAt ?? null, record.lastOfflineAt ?? null, record.lastPolicyPushAt ?? null, record.lastScreenshotAssetId ?? null, record.lastScreenshotCapturedAt ?? null);
|
|
342
431
|
}
|
|
343
432
|
async getPairedDevice(deviceId) {
|
|
344
433
|
const row = this.db.prepare(`
|
|
@@ -421,14 +510,13 @@ export class SQLiteStore {
|
|
|
421
510
|
`).get(id);
|
|
422
511
|
return row ? this.mapUploadedAssetRow(row) : undefined;
|
|
423
512
|
}
|
|
424
|
-
async getUploadedAssetBySha256(sha256
|
|
513
|
+
async getUploadedAssetBySha256(sha256) {
|
|
425
514
|
const row = this.db.prepare(`
|
|
426
515
|
SELECT *
|
|
427
516
|
FROM uploaded_assets
|
|
428
517
|
WHERE sha256 = ?
|
|
429
|
-
AND (? IS NULL OR storage_provider = ?)
|
|
430
518
|
LIMIT 1
|
|
431
|
-
`).get(sha256
|
|
519
|
+
`).get(sha256);
|
|
432
520
|
return row ? this.mapUploadedAssetRow(row) : undefined;
|
|
433
521
|
}
|
|
434
522
|
async setUploadedAsset(record) {
|
|
@@ -436,7 +524,6 @@ export class SQLiteStore {
|
|
|
436
524
|
INSERT INTO uploaded_assets (
|
|
437
525
|
id,
|
|
438
526
|
sha256,
|
|
439
|
-
storage_provider,
|
|
440
527
|
storage_key,
|
|
441
528
|
url,
|
|
442
529
|
original_filename,
|
|
@@ -446,10 +533,9 @@ export class SQLiteStore {
|
|
|
446
533
|
created_at,
|
|
447
534
|
updated_at
|
|
448
535
|
)
|
|
449
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
536
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
450
537
|
ON CONFLICT(id) DO UPDATE SET
|
|
451
538
|
sha256 = excluded.sha256,
|
|
452
|
-
storage_provider = excluded.storage_provider,
|
|
453
539
|
storage_key = excluded.storage_key,
|
|
454
540
|
url = excluded.url,
|
|
455
541
|
original_filename = excluded.original_filename,
|
|
@@ -457,7 +543,7 @@ export class SQLiteStore {
|
|
|
457
543
|
resource_type = excluded.resource_type,
|
|
458
544
|
bytes = excluded.bytes,
|
|
459
545
|
updated_at = excluded.updated_at
|
|
460
|
-
`).run(record.id, record.sha256, record.
|
|
546
|
+
`).run(record.id, record.sha256, record.storageKey, record.url, record.originalFilename ?? null, record.mimeType ?? null, record.resourceType ?? null, record.bytes ?? null, record.createdAt, record.updatedAt);
|
|
461
547
|
}
|
|
462
548
|
async deleteUploadedAsset(id) {
|
|
463
549
|
this.db.prepare("DELETE FROM uploaded_assets WHERE id = ?").run(id);
|
|
@@ -527,7 +613,9 @@ export class SQLiteStore {
|
|
|
527
613
|
lastBootAt: optionalString(row.last_boot_at),
|
|
528
614
|
lastOnlineAt: optionalString(row.last_online_at),
|
|
529
615
|
lastOfflineAt: optionalString(row.last_offline_at),
|
|
530
|
-
lastPolicyPushAt: optionalString(row.last_policy_push_at)
|
|
616
|
+
lastPolicyPushAt: optionalString(row.last_policy_push_at),
|
|
617
|
+
lastScreenshotAssetId: optionalString(row.last_screenshot_asset_id),
|
|
618
|
+
lastScreenshotCapturedAt: optionalString(row.last_screenshot_captured_at)
|
|
531
619
|
};
|
|
532
620
|
}
|
|
533
621
|
mapPlaylistRow(row) {
|
|
@@ -545,7 +633,6 @@ export class SQLiteStore {
|
|
|
545
633
|
return {
|
|
546
634
|
id: row.id,
|
|
547
635
|
sha256: row.sha256,
|
|
548
|
-
storageProvider: row.storage_provider,
|
|
549
636
|
storageKey: row.storage_key,
|
|
550
637
|
url: row.url,
|
|
551
638
|
originalFilename: optionalString(row.original_filename),
|
package/dist/store/types.d.ts
CHANGED
|
@@ -26,6 +26,9 @@ export interface PairedDeviceRecord {
|
|
|
26
26
|
lastOnlineAt?: string;
|
|
27
27
|
lastOfflineAt?: string;
|
|
28
28
|
lastPolicyPushAt?: string;
|
|
29
|
+
/** Latest device screenshot stored via uploaded_assets (local or Cloudinary). */
|
|
30
|
+
lastScreenshotAssetId?: string;
|
|
31
|
+
lastScreenshotCapturedAt?: string;
|
|
29
32
|
}
|
|
30
33
|
export interface PairedDeviceEntry {
|
|
31
34
|
deviceId: string;
|
|
@@ -45,11 +48,9 @@ export interface PlaylistItemRecord {
|
|
|
45
48
|
type?: string;
|
|
46
49
|
durationMs?: number;
|
|
47
50
|
}
|
|
48
|
-
export type UploadedAssetStorageProvider = "local" | "cloudinary";
|
|
49
51
|
export interface UploadedAssetRecord {
|
|
50
52
|
id: string;
|
|
51
53
|
sha256: string;
|
|
52
|
-
storageProvider: UploadedAssetStorageProvider;
|
|
53
54
|
storageKey: string;
|
|
54
55
|
url: string;
|
|
55
56
|
originalFilename?: string;
|
|
@@ -113,7 +114,7 @@ export interface TomorrowOSStore {
|
|
|
113
114
|
setPlaylist(record: StoredPlaylist): Promise<void>;
|
|
114
115
|
isPlaylistNameTaken(name: string, excludeId?: string): Promise<boolean>;
|
|
115
116
|
getUploadedAsset(id: string): Promise<UploadedAssetRecord | undefined>;
|
|
116
|
-
getUploadedAssetBySha256(sha256: string
|
|
117
|
+
getUploadedAssetBySha256(sha256: string): Promise<UploadedAssetRecord | undefined>;
|
|
117
118
|
setUploadedAsset(record: UploadedAssetRecord): Promise<void>;
|
|
118
119
|
deleteUploadedAsset(id: string): Promise<void>;
|
|
119
120
|
getDeviceAssignments(deviceId: string): Promise<DevicePlaylistAssignment[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.d.ts
CHANGED
|
@@ -78,6 +78,7 @@ export interface DeviceScreenshotInfo {
|
|
|
78
78
|
url: string;
|
|
79
79
|
capturedAt: string;
|
|
80
80
|
mimeType: string;
|
|
81
|
+
assetId?: string;
|
|
81
82
|
width?: number;
|
|
82
83
|
height?: number;
|
|
83
84
|
}
|
|
@@ -150,12 +151,13 @@ export declare class TomorrowOS extends EventEmitter {
|
|
|
150
151
|
private getAssetIdsFromItems;
|
|
151
152
|
private isUploadedAssetReferenced;
|
|
152
153
|
private releaseRemovedPlaylistAssets;
|
|
154
|
+
private isUploadedAssetReferencedAsDeviceScreenshot;
|
|
153
155
|
private deleteUploadedAssetIfUnreferenced;
|
|
154
156
|
private refreshResolvedBrandLogo;
|
|
155
157
|
private tryServeStatic;
|
|
156
|
-
private
|
|
157
|
-
private screenshotBaseName;
|
|
158
|
+
private getLegacyScreenshotsDir;
|
|
158
159
|
private screenshotExtension;
|
|
160
|
+
private getLegacyDeviceScreenshot;
|
|
159
161
|
private saveDeviceScreenshot;
|
|
160
162
|
private getLatestDeviceScreenshot;
|
|
161
163
|
private captureDeviceScreenshot;
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAKV,eAAe,
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -44,6 +44,9 @@ function normalizeDeviceSystemLabel(platform, system) {
|
|
|
44
44
|
const label = typeof system === "string" ? system.trim() : "";
|
|
45
45
|
return label || undefined;
|
|
46
46
|
}
|
|
47
|
+
function isRemoteUploadedAssetUrl(url) {
|
|
48
|
+
return /^https?:\/\//i.test(String(url || "").trim());
|
|
49
|
+
}
|
|
47
50
|
function formatDurationMs(ms) {
|
|
48
51
|
if (!Number.isFinite(ms) || ms < 0)
|
|
49
52
|
return "0s";
|
|
@@ -286,8 +289,13 @@ export class TomorrowOS extends EventEmitter {
|
|
|
286
289
|
err.code = "PAIRING_INVALID";
|
|
287
290
|
throw err;
|
|
288
291
|
}
|
|
292
|
+
const paired = await this.store.getPairedDevice(id);
|
|
293
|
+
const screenshotAssetId = paired?.lastScreenshotAssetId;
|
|
289
294
|
await this.store.deletePairedDevice(id);
|
|
290
295
|
this.pendingDeviceMeta.delete(id);
|
|
296
|
+
if (screenshotAssetId) {
|
|
297
|
+
await this.deleteUploadedAssetIfUnreferenced(screenshotAssetId);
|
|
298
|
+
}
|
|
291
299
|
const ws = this.devices.get(id);
|
|
292
300
|
let notified = false;
|
|
293
301
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
@@ -625,9 +633,7 @@ export class TomorrowOS extends EventEmitter {
|
|
|
625
633
|
}
|
|
626
634
|
this.staticIndexFile = idx;
|
|
627
635
|
const uploadsDir = path.join(path.resolve(this.staticRoot), "uploads");
|
|
628
|
-
const screenshotsDir = path.join(path.resolve(this.staticRoot), "screenshots");
|
|
629
636
|
void fs.mkdir(uploadsDir, { recursive: true });
|
|
630
|
-
void fs.mkdir(screenshotsDir, { recursive: true });
|
|
631
637
|
void this.refreshResolvedBrandLogo();
|
|
632
638
|
}
|
|
633
639
|
else {
|
|
@@ -685,10 +691,9 @@ export class TomorrowOS extends EventEmitter {
|
|
|
685
691
|
status: "success",
|
|
686
692
|
url: asset.url,
|
|
687
693
|
assetId: asset.id,
|
|
688
|
-
filename: asset.
|
|
694
|
+
filename: isRemoteUploadedAssetUrl(asset.url) ? safeName : asset.storageKey,
|
|
689
695
|
size: asset.bytes ?? body.length,
|
|
690
696
|
contentHash: asset.sha256,
|
|
691
|
-
storageProvider: asset.storageProvider,
|
|
692
697
|
deduplicated
|
|
693
698
|
};
|
|
694
699
|
if (durationMs != null)
|
|
@@ -703,12 +708,9 @@ export class TomorrowOS extends EventEmitter {
|
|
|
703
708
|
async storeUploadedMediaAsset(body, safeName, mimeType) {
|
|
704
709
|
const sha256 = contentHashHex(body);
|
|
705
710
|
const cloudinaryConfig = resolveCloudinaryConfig();
|
|
706
|
-
const
|
|
707
|
-
? "cloudinary"
|
|
708
|
-
: "local";
|
|
709
|
-
const existing = await this.store.getUploadedAssetBySha256(sha256, storageProvider);
|
|
711
|
+
const existing = await this.store.getUploadedAssetBySha256(sha256);
|
|
710
712
|
if (existing) {
|
|
711
|
-
if (existing.
|
|
713
|
+
if (isRemoteUploadedAssetUrl(existing.url)) {
|
|
712
714
|
return { asset: existing, deduplicated: true };
|
|
713
715
|
}
|
|
714
716
|
const existingLocalPath = path.join(path.resolve(this.staticRoot || "."), "uploads", existing.storageKey);
|
|
@@ -733,7 +735,6 @@ export class TomorrowOS extends EventEmitter {
|
|
|
733
735
|
const asset = {
|
|
734
736
|
id: randomUUID(),
|
|
735
737
|
sha256,
|
|
736
|
-
storageProvider: "cloudinary",
|
|
737
738
|
storageKey: uploaded.publicId,
|
|
738
739
|
url: uploaded.secureUrl,
|
|
739
740
|
originalFilename: safeName,
|
|
@@ -752,7 +753,6 @@ export class TomorrowOS extends EventEmitter {
|
|
|
752
753
|
const asset = {
|
|
753
754
|
id: existing?.id ?? randomUUID(),
|
|
754
755
|
sha256,
|
|
755
|
-
storageProvider: "local",
|
|
756
756
|
storageKey: storedName,
|
|
757
757
|
url: `/uploads/${storedName}`,
|
|
758
758
|
originalFilename: safeName,
|
|
@@ -790,14 +790,20 @@ export class TomorrowOS extends EventEmitter {
|
|
|
790
790
|
await this.deleteUploadedAssetIfUnreferenced(assetId);
|
|
791
791
|
}
|
|
792
792
|
}
|
|
793
|
+
async isUploadedAssetReferencedAsDeviceScreenshot(assetId) {
|
|
794
|
+
const devices = await this.store.listPairedDevices();
|
|
795
|
+
return devices.some((entry) => entry.record.lastScreenshotAssetId === assetId);
|
|
796
|
+
}
|
|
793
797
|
async deleteUploadedAssetIfUnreferenced(assetId) {
|
|
794
798
|
try {
|
|
795
799
|
if (await this.isUploadedAssetReferenced(assetId))
|
|
796
800
|
return;
|
|
801
|
+
if (await this.isUploadedAssetReferencedAsDeviceScreenshot(assetId))
|
|
802
|
+
return;
|
|
797
803
|
const asset = await this.store.getUploadedAsset(assetId);
|
|
798
804
|
if (!asset)
|
|
799
805
|
return;
|
|
800
|
-
if (asset.
|
|
806
|
+
if (isRemoteUploadedAssetUrl(asset.url)) {
|
|
801
807
|
const cloudinaryConfig = resolveCloudinaryConfig();
|
|
802
808
|
if (!cloudinaryConfig) {
|
|
803
809
|
console.warn(`[TomorrowOS] Cloudinary config missing; cannot delete asset ${asset.id}.`);
|
|
@@ -871,15 +877,12 @@ export class TomorrowOS extends EventEmitter {
|
|
|
871
877
|
return false;
|
|
872
878
|
}
|
|
873
879
|
}
|
|
874
|
-
|
|
880
|
+
getLegacyScreenshotsDir() {
|
|
875
881
|
if (!this.staticRoot) {
|
|
876
882
|
throw new Error("Screenshot storage requires listen({ staticRoot })");
|
|
877
883
|
}
|
|
878
884
|
return path.join(path.resolve(this.staticRoot), "screenshots");
|
|
879
885
|
}
|
|
880
|
-
screenshotBaseName(deviceId) {
|
|
881
|
-
return sanitizeStorageSegment(deviceId);
|
|
882
|
-
}
|
|
883
886
|
screenshotExtension(mimeType) {
|
|
884
887
|
const normalized = mimeType.toLowerCase();
|
|
885
888
|
if (normalized === "image/png")
|
|
@@ -888,6 +891,29 @@ export class TomorrowOS extends EventEmitter {
|
|
|
888
891
|
return ".webp";
|
|
889
892
|
return ".jpg";
|
|
890
893
|
}
|
|
894
|
+
async getLegacyDeviceScreenshot(deviceId) {
|
|
895
|
+
if (!this.staticRoot)
|
|
896
|
+
return null;
|
|
897
|
+
const base = sanitizeStorageSegment(deviceId);
|
|
898
|
+
const metaPath = path.join(this.getLegacyScreenshotsDir(), `${base}.json`);
|
|
899
|
+
try {
|
|
900
|
+
const raw = await fs.readFile(metaPath, "utf8");
|
|
901
|
+
const parsed = JSON.parse(raw);
|
|
902
|
+
if (!parsed || typeof parsed.url !== "string")
|
|
903
|
+
return null;
|
|
904
|
+
return {
|
|
905
|
+
deviceId,
|
|
906
|
+
url: parsed.url,
|
|
907
|
+
capturedAt: parsed.capturedAt,
|
|
908
|
+
mimeType: parsed.mimeType || "image/jpeg",
|
|
909
|
+
width: parsed.width,
|
|
910
|
+
height: parsed.height
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
catch {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
891
917
|
async saveDeviceScreenshot(deviceId, payload) {
|
|
892
918
|
const mimeType = typeof payload.mimeType === "string" && payload.mimeType.startsWith("image/")
|
|
893
919
|
? payload.mimeType
|
|
@@ -896,42 +922,51 @@ export class TomorrowOS extends EventEmitter {
|
|
|
896
922
|
if (!dataBase64.trim()) {
|
|
897
923
|
throw new Error("Screenshot payload missing dataBase64");
|
|
898
924
|
}
|
|
899
|
-
const dir = this.getScreenshotsDir();
|
|
900
|
-
await fs.mkdir(dir, { recursive: true });
|
|
901
|
-
const base = this.screenshotBaseName(deviceId);
|
|
902
|
-
const ext = this.screenshotExtension(mimeType);
|
|
903
|
-
const filename = `${base}${ext}`;
|
|
904
|
-
const filePath = path.join(dir, filename);
|
|
905
|
-
const metaPath = path.join(dir, `${base}.json`);
|
|
906
925
|
const capturedAt = typeof payload.capturedAt === "string" && payload.capturedAt.trim()
|
|
907
926
|
? payload.capturedAt
|
|
908
927
|
: new Date().toISOString();
|
|
909
|
-
|
|
910
|
-
const
|
|
928
|
+
const body = Buffer.from(dataBase64, "base64");
|
|
929
|
+
const safeName = sanitizeUploadFilename(`screenshot-${sanitizeStorageSegment(deviceId)}-${Date.now()}${this.screenshotExtension(mimeType)}`);
|
|
930
|
+
const paired = await this.store.getPairedDevice(deviceId);
|
|
931
|
+
const previousAssetId = paired?.lastScreenshotAssetId;
|
|
932
|
+
const { asset } = await this.storeUploadedMediaAsset(body, safeName, mimeType);
|
|
933
|
+
if (paired) {
|
|
934
|
+
await this.store.setPairedDevice(deviceId, {
|
|
935
|
+
...paired,
|
|
936
|
+
lastScreenshotAssetId: asset.id,
|
|
937
|
+
lastScreenshotCapturedAt: capturedAt
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
if (previousAssetId && previousAssetId !== asset.id) {
|
|
941
|
+
await this.deleteUploadedAssetIfUnreferenced(previousAssetId);
|
|
942
|
+
}
|
|
943
|
+
return {
|
|
911
944
|
deviceId,
|
|
912
|
-
|
|
945
|
+
assetId: asset.id,
|
|
946
|
+
url: asset.url,
|
|
913
947
|
capturedAt,
|
|
914
|
-
mimeType,
|
|
948
|
+
mimeType: asset.mimeType || mimeType,
|
|
915
949
|
width: typeof payload.width === "number" ? payload.width : undefined,
|
|
916
950
|
height: typeof payload.height === "number" ? payload.height : undefined
|
|
917
951
|
};
|
|
918
|
-
await fs.writeFile(metaPath, JSON.stringify(info, null, 2));
|
|
919
|
-
return info;
|
|
920
952
|
}
|
|
921
953
|
async getLatestDeviceScreenshot(deviceId) {
|
|
922
|
-
const
|
|
923
|
-
const
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
954
|
+
const paired = await this.store.getPairedDevice(deviceId);
|
|
955
|
+
const assetId = paired?.lastScreenshotAssetId;
|
|
956
|
+
const capturedAt = paired?.lastScreenshotCapturedAt;
|
|
957
|
+
if (assetId && capturedAt) {
|
|
958
|
+
const asset = await this.store.getUploadedAsset(assetId);
|
|
959
|
+
if (asset) {
|
|
960
|
+
return {
|
|
961
|
+
deviceId,
|
|
962
|
+
assetId: asset.id,
|
|
963
|
+
url: asset.url,
|
|
964
|
+
capturedAt,
|
|
965
|
+
mimeType: asset.mimeType || "image/jpeg"
|
|
966
|
+
};
|
|
967
|
+
}
|
|
934
968
|
}
|
|
969
|
+
return this.getLegacyDeviceScreenshot(deviceId);
|
|
935
970
|
}
|
|
936
971
|
async captureDeviceScreenshot(deviceId) {
|
|
937
972
|
const result = await this.sendDeviceCommand(deviceId, "device.captureScreen", {});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "TomorrowOS CMS server SDK - WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build-player": "tomorrowos build --platform tizen"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@tomorrowos/sdk": "^0.8.
|
|
16
|
+
"@tomorrowos/sdk": "^0.8.5",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
18
|
"tsx": "^4.19.0"
|
|
19
19
|
},
|
|
@@ -155,6 +155,23 @@
|
|
|
155
155
|
</div>
|
|
156
156
|
</div>
|
|
157
157
|
|
|
158
|
+
<div id="downloadFailedModal" class="modal hidden" role="dialog" aria-modal="true">
|
|
159
|
+
<div class="modal-backdrop" data-close-download-failed-modal="1"></div>
|
|
160
|
+
<div class="modal-card">
|
|
161
|
+
<h2>Download failed</h2>
|
|
162
|
+
<p class="hint" id="downloadFailedModalMessage">Could not download this asset.</p>
|
|
163
|
+
<p class="hint">
|
|
164
|
+
For permanent, reliable storage of media assets, configure
|
|
165
|
+
<strong>Cloudinary</strong> on your CMS server (<code>CLOUDINARY_CLOUD_NAME</code>,
|
|
166
|
+
<code>CLOUDINARY_API_KEY</code>, <code>CLOUDINARY_API_SECRET</code>). Uploads stored on
|
|
167
|
+
Cloudinary use stable public URLs that are easier to download and share.
|
|
168
|
+
</p>
|
|
169
|
+
<div class="modal-actions">
|
|
170
|
+
<button type="button" data-close-download-failed-modal="1">Close</button>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
158
175
|
<script src="./methods.js" defer></script>
|
|
159
176
|
</body>
|
|
160
177
|
</html>
|
|
@@ -808,6 +808,12 @@ function renderEditorAssets() {
|
|
|
808
808
|
meta.textContent = `${item.type} · ${Math.round(item.durationMs / 1000)}s`;
|
|
809
809
|
});
|
|
810
810
|
|
|
811
|
+
const downloadBtn = document.createElement("button");
|
|
812
|
+
downloadBtn.type = "button";
|
|
813
|
+
downloadBtn.textContent = "Download";
|
|
814
|
+
downloadBtn.title = "Download asset";
|
|
815
|
+
downloadBtn.addEventListener("click", () => void downloadMediaAsset(item));
|
|
816
|
+
|
|
811
817
|
const removeBtn = document.createElement("button");
|
|
812
818
|
removeBtn.type = "button";
|
|
813
819
|
removeBtn.className = "danger";
|
|
@@ -818,6 +824,7 @@ function renderEditorAssets() {
|
|
|
818
824
|
});
|
|
819
825
|
|
|
820
826
|
actions.appendChild(durInput);
|
|
827
|
+
actions.appendChild(downloadBtn);
|
|
821
828
|
actions.appendChild(removeBtn);
|
|
822
829
|
li.appendChild(name);
|
|
823
830
|
li.appendChild(meta);
|
|
@@ -1679,6 +1686,56 @@ function closeScreenshotModal() {
|
|
|
1679
1686
|
modal?.classList.add("hidden");
|
|
1680
1687
|
}
|
|
1681
1688
|
|
|
1689
|
+
function openDownloadFailedModal(detail) {
|
|
1690
|
+
const modal = document.getElementById("downloadFailedModal");
|
|
1691
|
+
const message = document.getElementById("downloadFailedModalMessage");
|
|
1692
|
+
if (!modal) return;
|
|
1693
|
+
if (message) {
|
|
1694
|
+
message.textContent = detail
|
|
1695
|
+
? `Could not download this asset: ${detail}`
|
|
1696
|
+
: "Could not download this asset.";
|
|
1697
|
+
}
|
|
1698
|
+
modal.classList.remove("hidden");
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
function closeDownloadFailedModal() {
|
|
1702
|
+
document.getElementById("downloadFailedModal")?.classList.add("hidden");
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
function sanitizeDownloadFilename(name) {
|
|
1706
|
+
const base = String(name || "asset").trim() || "asset";
|
|
1707
|
+
return base.replace(/[<>:"/\\|?*\x00-\x1f]/g, "_");
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
async function downloadMediaAsset(item) {
|
|
1711
|
+
let url;
|
|
1712
|
+
try {
|
|
1713
|
+
url = absoluteMediaUrl(item.url);
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
openDownloadFailedModal(err?.message || "Media URL is not available.");
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
const filename = sanitizeDownloadFilename(item.name || item.url.split("/").pop() || "asset");
|
|
1720
|
+
|
|
1721
|
+
try {
|
|
1722
|
+
const res = await fetch(url);
|
|
1723
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1724
|
+
const blob = await res.blob();
|
|
1725
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
1726
|
+
const anchor = document.createElement("a");
|
|
1727
|
+
anchor.href = objectUrl;
|
|
1728
|
+
anchor.download = filename;
|
|
1729
|
+
anchor.style.display = "none";
|
|
1730
|
+
document.body.appendChild(anchor);
|
|
1731
|
+
anchor.click();
|
|
1732
|
+
anchor.remove();
|
|
1733
|
+
URL.revokeObjectURL(objectUrl);
|
|
1734
|
+
} catch (err) {
|
|
1735
|
+
openDownloadFailedModal(err?.message || "Network or browser error.");
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1682
1739
|
function startDevicePolling() {
|
|
1683
1740
|
if (devicePollTimer) clearInterval(devicePollTimer);
|
|
1684
1741
|
void fetchDevices();
|
|
@@ -1710,6 +1767,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
1710
1767
|
document.querySelectorAll("[data-close-screenshot-modal]").forEach((el) => {
|
|
1711
1768
|
el.addEventListener("click", closeScreenshotModal);
|
|
1712
1769
|
});
|
|
1770
|
+
document.querySelectorAll("[data-close-download-failed-modal]").forEach((el) => {
|
|
1771
|
+
el.addEventListener("click", closeDownloadFailedModal);
|
|
1772
|
+
});
|
|
1713
1773
|
|
|
1714
1774
|
document.getElementById("addAssetBtn")?.addEventListener("click", () => {
|
|
1715
1775
|
if (uploadInProgress) return;
|