@_nazmiforreal/flutter-ota 0.1.19 → 0.1.21
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/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +4 -3
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -2
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +22 -10
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +16 -5
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +3 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +7 -8
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +12 -3
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +129 -6
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +2 -1
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +23 -14
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +4 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +266 -26
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1422 -16
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +4 -1
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +10 -9
- package/dart-src/packages/cli-tools/lib/src/config.dart +14 -19
- package/dart-src/packages/cli-tools/lib/src/operations.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +1 -1
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +113 -10
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +59 -6
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +2 -2
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +149 -0
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +14 -9
- package/dart-src/packages/core/CHANGELOG.md +6 -1
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +2 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +73 -1
- package/dart-src/packages/core/lib/src/bundle.dart +62 -17
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +7 -3
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +16 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +33 -3
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +24 -2
- package/dart-src/packages/core/lib/src/platform.dart +8 -0
- package/dart-src/packages/core/lib/src/semver.dart +0 -3
- package/dart-src/packages/core/lib/src/status.dart +20 -1
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +15 -0
- package/dart-src/packages/core/lib/src/uuid.dart +11 -0
- package/dart-src/plugins/aws/CHANGELOG.md +5 -1
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +2 -2
- package/dart-src/plugins/cloudflare/CHANGELOG.md +5 -1
- package/dart-src/plugins/plugin-core/CHANGELOG.md +5 -1
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +3 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +17 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +94 -3
- package/dart-src/plugins/pocketbase/CHANGELOG.md +5 -1
- package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +2 -1
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +10 -2
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +617 -4
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +262 -42
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +9 -2
- package/dart-src/plugins/postgres/CHANGELOG.md +5 -1
- package/dart-src/plugins/supabase/CHANGELOG.md +6 -1
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +4 -0
- package/package.json +2 -2
- package/bin/flutter-ota-linux-x64 +0 -0
- package/bin/flutter-ota.js +0 -36
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +0 -458
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +0 -707
- package/dart-src/packages/cli-tools/pubspec.lock +0 -590
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/// PocketBase-backed [DatabasePlugin] implementation.
|
|
2
|
+
///
|
|
3
|
+
/// Implements full parity with the Cloudflare/Supabase backends including
|
|
4
|
+
/// proper `getUpdateInfo` with `UpdateStrategy` branching, `bundle_patches`
|
|
5
|
+
/// support, and comprehensive filter operators.
|
|
2
6
|
library;
|
|
3
7
|
|
|
4
8
|
import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
|
|
@@ -33,20 +37,69 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
33
37
|
@override
|
|
34
38
|
bool get supportsCursorPagination => false;
|
|
35
39
|
|
|
40
|
+
// ----- Bundle patches -----
|
|
41
|
+
|
|
42
|
+
String get _patchesCollection => '${config.bundlesCollection}_patches';
|
|
43
|
+
|
|
44
|
+
Future<Map<String, List<Map<String, dynamic>>>> _getPatchMap(
|
|
45
|
+
List<String> bundleIds,
|
|
46
|
+
) async {
|
|
47
|
+
final patchMap = <String, List<Map<String, dynamic>>>{};
|
|
48
|
+
if (bundleIds.isEmpty) return patchMap;
|
|
49
|
+
|
|
50
|
+
final filter = bundleIds
|
|
51
|
+
.map((id) => 'bundle_id = "${_escape(id)}"')
|
|
52
|
+
.join(' || ');
|
|
53
|
+
try {
|
|
54
|
+
final res = await client.listRecords<dynamic>(
|
|
55
|
+
_patchesCollection,
|
|
56
|
+
(j) => j,
|
|
57
|
+
filter: filter.isEmpty ? null : filter,
|
|
58
|
+
sort: 'order_index',
|
|
59
|
+
perPage: 500,
|
|
60
|
+
);
|
|
61
|
+
for (final item in res.items) {
|
|
62
|
+
final row = item as Map<String, dynamic>;
|
|
63
|
+
final bundleId = row['bundle_id'] as String? ?? '';
|
|
64
|
+
if (bundleId.isNotEmpty) {
|
|
65
|
+
(patchMap[bundleId] ??= []).add(row);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (_) {
|
|
69
|
+
// patches collection may not exist yet; treat as no patches.
|
|
70
|
+
}
|
|
71
|
+
return patchMap;
|
|
72
|
+
}
|
|
73
|
+
|
|
36
74
|
// ----- Channels -----
|
|
37
75
|
|
|
38
76
|
@override
|
|
39
77
|
Future<List<String>> getChannels() async {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
try {
|
|
79
|
+
final res = await client.listRecords<dynamic>(
|
|
80
|
+
config.channelsCollection,
|
|
81
|
+
(j) => j,
|
|
82
|
+
perPage: 200,
|
|
83
|
+
sort: 'name',
|
|
84
|
+
);
|
|
85
|
+
return res.items
|
|
86
|
+
.map((j) => (j as Map)['name'] as String? ?? '')
|
|
87
|
+
.where((s) => s.isNotEmpty)
|
|
88
|
+
.toList();
|
|
89
|
+
} catch (_) {
|
|
90
|
+
// Fallback: extract distinct channels from bundles.
|
|
91
|
+
final res = await client.listRecords<PocketBaseBundleRow>(
|
|
92
|
+
config.bundlesCollection,
|
|
93
|
+
PocketBaseBundleRow.fromJson,
|
|
94
|
+
perPage: 500,
|
|
95
|
+
sort: '-created',
|
|
96
|
+
);
|
|
97
|
+
final channels = <String>{};
|
|
98
|
+
for (final b in res.items) {
|
|
99
|
+
if (b.channel.isNotEmpty) channels.add(b.channel);
|
|
100
|
+
}
|
|
101
|
+
return channels.toList()..sort();
|
|
102
|
+
}
|
|
50
103
|
}
|
|
51
104
|
|
|
52
105
|
// ----- Single bundle -----
|
|
@@ -58,32 +111,134 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
58
111
|
bundleId,
|
|
59
112
|
PocketBaseBundleRow.fromJson,
|
|
60
113
|
);
|
|
61
|
-
|
|
114
|
+
if (row == null) return null;
|
|
115
|
+
final patchMap = await _getPatchMap([bundleId]);
|
|
116
|
+
return mapRowToBundle(row, patches: patchMap[bundleId]);
|
|
62
117
|
}
|
|
63
118
|
|
|
119
|
+
// ----- Update info -----
|
|
120
|
+
|
|
64
121
|
@override
|
|
65
122
|
Future<UpdateInfo?> getUpdateInfo(GetBundlesArgs args) async {
|
|
123
|
+
final channel = args.channel;
|
|
124
|
+
final minBundleId = args.minBundleId;
|
|
125
|
+
final platform = args.platform.value;
|
|
126
|
+
|
|
127
|
+
if (args.updateStrategy == UpdateStrategy.appVersion) {
|
|
128
|
+
final appVersion = (args as AppVersionGetBundlesArgs).appVersion;
|
|
129
|
+
|
|
130
|
+
// 1. Collect distinct target_app_versions from enabled bundles.
|
|
131
|
+
final allRes = await client.listRecords<PocketBaseBundleRow>(
|
|
132
|
+
config.bundlesCollection,
|
|
133
|
+
PocketBaseBundleRow.fromJson,
|
|
134
|
+
filter: _buildFilter(
|
|
135
|
+
channel: channel,
|
|
136
|
+
platformValue: platform,
|
|
137
|
+
enabled: true,
|
|
138
|
+
idGte: minBundleId,
|
|
139
|
+
targetAppVersionNotNull: true,
|
|
140
|
+
),
|
|
141
|
+
sort: '-created',
|
|
142
|
+
perPage: 500,
|
|
143
|
+
);
|
|
144
|
+
final targetVersions = allRes.items
|
|
145
|
+
.map((b) => b.targetAppVersion)
|
|
146
|
+
.whereType<String>()
|
|
147
|
+
.toSet()
|
|
148
|
+
.toList();
|
|
149
|
+
|
|
150
|
+
// 2. Filter to compatible versions.
|
|
151
|
+
final compatible = filterCompatibleAppVersions(
|
|
152
|
+
targetVersions,
|
|
153
|
+
appVersion,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (compatible.isEmpty) {
|
|
157
|
+
// No compatible bundles — check if we should rollback.
|
|
158
|
+
if (args.bundleId == nilUuid ||
|
|
159
|
+
(minBundleId.isNotEmpty &&
|
|
160
|
+
args.bundleId.compareTo(minBundleId) <= 0)) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
return const UpdateInfo(
|
|
164
|
+
id: nilUuid,
|
|
165
|
+
shouldForceUpdate: true,
|
|
166
|
+
message: null,
|
|
167
|
+
status: UpdateStatus.rollback,
|
|
168
|
+
storageUri: null,
|
|
169
|
+
fileHash: null,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 3. Query bundles with compatible target_app_version using ?= operator.
|
|
174
|
+
final filter = _buildFilter(
|
|
175
|
+
channel: channel,
|
|
176
|
+
platformValue: platform,
|
|
177
|
+
enabled: true,
|
|
178
|
+
idGte: minBundleId,
|
|
179
|
+
targetAppVersionIn: compatible,
|
|
180
|
+
);
|
|
181
|
+
final res = await client.listRecords<PocketBaseBundleRow>(
|
|
182
|
+
config.bundlesCollection,
|
|
183
|
+
PocketBaseBundleRow.fromJson,
|
|
184
|
+
filter: filter.isEmpty ? null : filter,
|
|
185
|
+
sort: '-created',
|
|
186
|
+
perPage: 500,
|
|
187
|
+
);
|
|
188
|
+
final bundles = res.items.map(mapRowToBundle).toList();
|
|
189
|
+
final patchMap = await _getPatchMap(bundles.map((b) => b.id).toList());
|
|
190
|
+
final bundlesWithPatches = bundles
|
|
191
|
+
.map(
|
|
192
|
+
(b) => mapRowToBundle(
|
|
193
|
+
res.items.firstWhere((r) => r.id == b.id),
|
|
194
|
+
patches: patchMap[b.id],
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
.toList();
|
|
198
|
+
|
|
199
|
+
return resolveUpdateInfoFromBundles(
|
|
200
|
+
ResolveUpdateInfoFromBundlesOptions(
|
|
201
|
+
args: args,
|
|
202
|
+
bundles: bundlesWithPatches,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Fingerprint strategy.
|
|
208
|
+
final fingerprintHash = args is FingerprintGetBundlesArgs
|
|
209
|
+
? args.fingerprintHash
|
|
210
|
+
: null;
|
|
211
|
+
|
|
66
212
|
final filter = _buildFilter(
|
|
67
|
-
channel:
|
|
68
|
-
|
|
213
|
+
channel: channel,
|
|
214
|
+
platformValue: platform,
|
|
69
215
|
enabled: true,
|
|
216
|
+
idGte: minBundleId,
|
|
217
|
+
fingerprintHash: fingerprintHash,
|
|
70
218
|
);
|
|
71
|
-
final
|
|
219
|
+
final res = await client.listRecords<PocketBaseBundleRow>(
|
|
72
220
|
config.bundlesCollection,
|
|
73
221
|
PocketBaseBundleRow.fromJson,
|
|
74
222
|
filter: filter.isEmpty ? null : filter,
|
|
75
223
|
sort: '-created',
|
|
76
|
-
perPage:
|
|
224
|
+
perPage: 500,
|
|
77
225
|
);
|
|
78
|
-
|
|
79
|
-
final
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
226
|
+
final bundles = res.items.map(mapRowToBundle).toList();
|
|
227
|
+
final patchMap = await _getPatchMap(bundles.map((b) => b.id).toList());
|
|
228
|
+
final bundlesWithPatches = bundles
|
|
229
|
+
.map(
|
|
230
|
+
(b) => mapRowToBundle(
|
|
231
|
+
res.items.firstWhere((r) => r.id == b.id),
|
|
232
|
+
patches: patchMap[b.id],
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
.toList();
|
|
236
|
+
|
|
237
|
+
return resolveUpdateInfoFromBundles(
|
|
238
|
+
ResolveUpdateInfoFromBundlesOptions(
|
|
239
|
+
args: args,
|
|
240
|
+
bundles: bundlesWithPatches,
|
|
241
|
+
),
|
|
87
242
|
);
|
|
88
243
|
}
|
|
89
244
|
|
|
@@ -96,17 +251,18 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
96
251
|
final where = options.where;
|
|
97
252
|
final filter = _buildFilter(
|
|
98
253
|
channel: where?.channel,
|
|
99
|
-
|
|
254
|
+
platformValue: where?.platform?.value,
|
|
100
255
|
enabled: where?.enabled,
|
|
101
256
|
idEq: where?.id?.eq,
|
|
102
257
|
idGt: where?.id?.gt,
|
|
103
258
|
idLt: where?.id?.lt,
|
|
104
259
|
targetAppVersion: where?.targetAppVersion,
|
|
260
|
+
targetAppVersionIn: where?.targetAppVersionIn,
|
|
261
|
+
targetAppVersionNotNull: where?.targetAppVersionNotNull,
|
|
105
262
|
fingerprintHash: where?.fingerprintHash,
|
|
106
263
|
);
|
|
107
264
|
final orderField = options.orderBy?.field ?? 'id';
|
|
108
265
|
final desc = options.orderBy?.direction != 'asc';
|
|
109
|
-
// PB sort syntax: `field` for asc, `-field` for desc.
|
|
110
266
|
final sort = (desc ? '-' : '') + orderField;
|
|
111
267
|
final limit = options.limit == 0 ? 50 : options.limit;
|
|
112
268
|
final page = (options.offset ?? 0) ~/ limit + 1;
|
|
@@ -118,13 +274,16 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
118
274
|
page: page,
|
|
119
275
|
perPage: limit,
|
|
120
276
|
);
|
|
277
|
+
final patchMap = await _getPatchMap(list.items.map((b) => b.id).toList());
|
|
121
278
|
final total = list.totalItems;
|
|
122
279
|
final totalPages = list.totalPages == 0 ? 1 : list.totalPages;
|
|
123
280
|
final currentPage = list.page;
|
|
124
281
|
final hasNextPage = currentPage < totalPages;
|
|
125
282
|
final hasPreviousPage = currentPage > 1;
|
|
126
283
|
return Paginated(
|
|
127
|
-
data: list.items
|
|
284
|
+
data: list.items
|
|
285
|
+
.map((row) => mapRowToBundle(row, patches: patchMap[row.id]))
|
|
286
|
+
.toList(),
|
|
128
287
|
pagination: PaginationInfo(
|
|
129
288
|
total: total,
|
|
130
289
|
hasNextPage: hasNextPage,
|
|
@@ -154,9 +313,6 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
154
313
|
change.data.id,
|
|
155
314
|
);
|
|
156
315
|
if (exists) {
|
|
157
|
-
// The storage plugin may have pre-created the record (to host
|
|
158
|
-
// the artifact file). Update the metadata fields rather than
|
|
159
|
-
// failing on a duplicate id.
|
|
160
316
|
await client.updateRecord<dynamic>(
|
|
161
317
|
config.bundlesCollection,
|
|
162
318
|
change.data.id,
|
|
@@ -170,24 +326,66 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
170
326
|
(j) => j,
|
|
171
327
|
);
|
|
172
328
|
}
|
|
173
|
-
|
|
329
|
+
// Upsert patches.
|
|
330
|
+
await _commitPatches(change.data);
|
|
331
|
+
|
|
174
332
|
case BundleChangeOperation.update:
|
|
175
333
|
final patch = _buildUpdatePatch(change.data);
|
|
176
|
-
if (patch.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
334
|
+
if (patch.isNotEmpty) {
|
|
335
|
+
await client.updateRecord<dynamic>(
|
|
336
|
+
config.bundlesCollection,
|
|
337
|
+
change.data.id,
|
|
338
|
+
patch,
|
|
339
|
+
(j) => j,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
await _commitPatches(change.data);
|
|
343
|
+
|
|
184
344
|
case BundleChangeOperation.delete:
|
|
345
|
+
// Delete associated patches first.
|
|
346
|
+
await _deletePatches(change.data.id);
|
|
185
347
|
await client.deleteRecord(config.bundlesCollection, change.data.id);
|
|
186
|
-
break;
|
|
187
348
|
}
|
|
188
349
|
}
|
|
189
350
|
}
|
|
190
351
|
|
|
352
|
+
Future<void> _commitPatches(Bundle bundle) async {
|
|
353
|
+
final patches = bundle.patches;
|
|
354
|
+
if (patches == null || patches.isEmpty) return;
|
|
355
|
+
// Delete existing patches for this bundle, then re-create.
|
|
356
|
+
await _deletePatches(bundle.id);
|
|
357
|
+
for (var i = 0; i < patches.length; i++) {
|
|
358
|
+
final p = patches[i];
|
|
359
|
+
await client.createRecord<dynamic>(_patchesCollection, {
|
|
360
|
+
'bundle_id': bundle.id,
|
|
361
|
+
'base_bundle_id': p.baseBundleId,
|
|
362
|
+
'base_file_hash': p.baseFileHash,
|
|
363
|
+
'patch_file_hash': p.patchFileHash,
|
|
364
|
+
'patch_storage_uri': p.patchStorageUri,
|
|
365
|
+
'order_index': i,
|
|
366
|
+
}, (j) => j);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Future<void> _deletePatches(String bundleId) async {
|
|
371
|
+
try {
|
|
372
|
+
final res = await client.listRecords<dynamic>(
|
|
373
|
+
_patchesCollection,
|
|
374
|
+
(j) => j,
|
|
375
|
+
filter: 'bundle_id = "${_escape(bundleId)}"',
|
|
376
|
+
perPage: 500,
|
|
377
|
+
);
|
|
378
|
+
for (final item in res.items) {
|
|
379
|
+
final id = (item as Map)['id'] as String?;
|
|
380
|
+
if (id != null) {
|
|
381
|
+
await client.deleteRecord(_patchesCollection, id);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} catch (_) {
|
|
385
|
+
// patches collection may not exist; ignore.
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
191
389
|
@override
|
|
192
390
|
Future<void> onUnmount() async {
|
|
193
391
|
client.close();
|
|
@@ -217,30 +415,52 @@ class _PocketBaseDatabase implements AbstractDatabasePlugin {
|
|
|
217
415
|
|
|
218
416
|
String _buildFilter({
|
|
219
417
|
String? channel,
|
|
418
|
+
String? platformValue,
|
|
220
419
|
Platform? platform,
|
|
221
420
|
bool? enabled,
|
|
222
421
|
String? idEq,
|
|
223
422
|
String? idGt,
|
|
423
|
+
String? idGte,
|
|
224
424
|
String? idLt,
|
|
425
|
+
String? idLte,
|
|
426
|
+
List<String>? idIns,
|
|
225
427
|
String? targetAppVersion,
|
|
428
|
+
List<String>? targetAppVersionIn,
|
|
429
|
+
bool? targetAppVersionNotNull,
|
|
226
430
|
String? fingerprintHash,
|
|
227
431
|
}) {
|
|
228
432
|
final clauses = <String>[];
|
|
229
433
|
if (channel != null && channel.isNotEmpty) {
|
|
230
434
|
clauses.add('channel = "${_escape(channel)}"');
|
|
231
435
|
}
|
|
232
|
-
|
|
233
|
-
|
|
436
|
+
final pv = platformValue ?? platform?.value;
|
|
437
|
+
if (pv != null) {
|
|
438
|
+
clauses.add('platform = "${_escape(pv)}"');
|
|
234
439
|
}
|
|
235
440
|
if (enabled != null) {
|
|
236
441
|
clauses.add('enabled = ${enabled ? 'true' : 'false'}');
|
|
237
442
|
}
|
|
238
443
|
if (idEq != null) clauses.add('id = "${_escape(idEq)}"');
|
|
239
444
|
if (idGt != null) clauses.add('id > "${_escape(idGt)}"');
|
|
445
|
+
if (idGte != null) clauses.add('id >= "${_escape(idGte)}"');
|
|
240
446
|
if (idLt != null) clauses.add('id < "${_escape(idLt)}"');
|
|
447
|
+
if (idLte != null) clauses.add('id <= "${_escape(idLte)}"');
|
|
448
|
+
if (idIns != null && idIns.isNotEmpty) {
|
|
449
|
+
final escaped = idIns.map((id) => '"${_escape(id)}"').join(',');
|
|
450
|
+
clauses.add('id ?= [$escaped]');
|
|
451
|
+
}
|
|
241
452
|
if (targetAppVersion != null) {
|
|
242
453
|
clauses.add('target_app_version = "${_escape(targetAppVersion)}"');
|
|
243
454
|
}
|
|
455
|
+
if (targetAppVersionIn != null && targetAppVersionIn.isNotEmpty) {
|
|
456
|
+
final escaped = targetAppVersionIn
|
|
457
|
+
.map((v) => '"${_escape(v)}"')
|
|
458
|
+
.join(',');
|
|
459
|
+
clauses.add('target_app_version ?= [$escaped]');
|
|
460
|
+
}
|
|
461
|
+
if (targetAppVersionNotNull == true) {
|
|
462
|
+
clauses.add('target_app_version != ""');
|
|
463
|
+
}
|
|
244
464
|
if (fingerprintHash != null) {
|
|
245
465
|
clauses.add('fingerprint_hash = "${_escape(fingerprintHash)}"');
|
|
246
466
|
}
|
|
@@ -109,8 +109,15 @@ class _PocketBaseRuntimeStorage implements RuntimeStorageProfile {
|
|
|
109
109
|
|
|
110
110
|
@override
|
|
111
111
|
Future<String?> readText(String storageUri) async {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
try {
|
|
113
|
+
final urlData = await getDownloadUrl(storageUri);
|
|
114
|
+
final url = urlData['fileUrl'];
|
|
115
|
+
if (url == null || url.isEmpty) return null;
|
|
116
|
+
final bytes = await _client.downloadFile(url);
|
|
117
|
+
return String.fromCharCodes(bytes);
|
|
118
|
+
} catch (_) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -23,6 +23,7 @@ class _SupabaseUpdateInfoRow {
|
|
|
23
23
|
final String status;
|
|
24
24
|
final String? storageUri;
|
|
25
25
|
final String? fileHash;
|
|
26
|
+
final String? gitCommitHash;
|
|
26
27
|
|
|
27
28
|
const _SupabaseUpdateInfoRow({
|
|
28
29
|
required this.id,
|
|
@@ -31,6 +32,7 @@ class _SupabaseUpdateInfoRow {
|
|
|
31
32
|
required this.status,
|
|
32
33
|
required this.storageUri,
|
|
33
34
|
required this.fileHash,
|
|
35
|
+
this.gitCommitHash,
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -65,6 +67,7 @@ UpdateInfo mapUpdateInfoRow(_SupabaseUpdateInfoRow row) => UpdateInfo(
|
|
|
65
67
|
: UpdateStatus.update,
|
|
66
68
|
storageUri: row.storageUri,
|
|
67
69
|
fileHash: row.fileHash,
|
|
70
|
+
gitCommitHash: row.gitCommitHash,
|
|
68
71
|
);
|
|
69
72
|
|
|
70
73
|
/// Supabase-backed [DatabasePlugin] factory.
|
|
@@ -213,6 +216,7 @@ class _SupabaseDatabasePlugin implements AbstractDatabasePlugin {
|
|
|
213
216
|
status: row['status'] as String,
|
|
214
217
|
storageUri: row['storage_uri'] as String?,
|
|
215
218
|
fileHash: row['file_hash'] as String?,
|
|
219
|
+
gitCommitHash: row['git_commit_hash'] as String?,
|
|
216
220
|
);
|
|
217
221
|
}
|
|
218
222
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@_nazmiforreal/flutter-ota",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Command-line interface for flutter_ota_kit
|
|
3
|
+
"version": "0.1.21",
|
|
4
|
+
"description": "Command-line interface for flutter_ota_kit — an OTA code-push tool for Flutter, hot-updater compatible.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"flutter-ota": "./bin/flutter-ota.js"
|
|
7
7
|
},
|
|
Binary file
|
package/bin/flutter-ota.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const { execFileSync } = require('child_process');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
|
|
8
|
-
const platform = os.platform();
|
|
9
|
-
const arch = os.arch();
|
|
10
|
-
|
|
11
|
-
const binaryNames = {
|
|
12
|
-
'linux-x64': 'flutter-ota-linux-x64',
|
|
13
|
-
'linux-arm64': 'flutter-ota-linux-arm64',
|
|
14
|
-
'darwin-x64': 'flutter-ota-darwin-x64',
|
|
15
|
-
'darwin-arm64': 'flutter-ota-darwin-arm64',
|
|
16
|
-
'win32-x64': 'flutter-ota-win32-x64.exe',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const key = `${platform}-${arch}`;
|
|
20
|
-
const binName = binaryNames[key];
|
|
21
|
-
|
|
22
|
-
if (!binName) {
|
|
23
|
-
console.error(`flutter-ota: unsupported platform ${platform}-${arch}`);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const binPath = path.join(__dirname, binName);
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
execFileSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
31
|
-
} catch (err) {
|
|
32
|
-
if (err.status !== undefined) {
|
|
33
|
-
process.exit(err.status);
|
|
34
|
-
}
|
|
35
|
-
throw err;
|
|
36
|
-
}
|