@_nazmiforreal/flutter-ota 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/flutter-patcher.js +48 -39
- package/dart-src/packages/cli-tools/.gitkeep +0 -0
- package/dart-src/packages/cli-tools/bin/flutter_patcher.dart +9 -0
- package/dart-src/packages/cli-tools/lib/flutter_patcher_cli.dart +73 -0
- package/dart-src/packages/cli-tools/lib/src/backend.dart +162 -0
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +56 -0
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +99 -0
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +151 -0
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +140 -0
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +113 -0
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +95 -0
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +48 -0
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +29 -0
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +82 -0
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +55 -0
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +132 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +41 -0
- package/dart-src/packages/cli-tools/lib/src/config.dart +715 -0
- package/dart-src/packages/cli-tools/lib/src/operations.dart +206 -0
- package/dart-src/packages/cli-tools/lib/src/pack.dart +417 -0
- package/dart-src/packages/cli-tools/lib/src/sign.dart +60 -0
- package/dart-src/packages/cli-tools/lib/src/util.dart +86 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0001_hot-updater_init.sql +16 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/packages/cli-tools/migrations/cloudflare/bundles.sql +42 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250103114225_init.sql +228 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/packages/cli-tools/migrations/supabase/20260520014100_hot-updater_rls.sql +48 -0
- package/dart-src/packages/cli-tools/pubspec.yaml +37 -0
- package/dart-src/packages/core/lib/flutter_patcher_core.dart +29 -0
- package/dart-src/packages/core/lib/src/app_update_info.dart +111 -0
- package/dart-src/packages/core/lib/src/bundle.dart +191 -0
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +39 -0
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +47 -0
- package/dart-src/packages/core/lib/src/changed_asset.dart +77 -0
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +78 -0
- package/dart-src/packages/core/lib/src/metadata.dart +31 -0
- package/dart-src/packages/core/lib/src/patch_info.dart +284 -0
- package/dart-src/packages/core/lib/src/platform.dart +15 -0
- package/dart-src/packages/core/lib/src/rollout.dart +209 -0
- package/dart-src/packages/core/lib/src/semver.dart +70 -0
- package/dart-src/packages/core/lib/src/semver_range.dart +358 -0
- package/dart-src/packages/core/lib/src/semver_version.dart +94 -0
- package/dart-src/packages/core/lib/src/status.dart +24 -0
- package/dart-src/packages/core/lib/src/strategy.dart +10 -0
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +30 -0
- package/dart-src/packages/core/lib/src/uuid.dart +45 -0
- package/dart-src/packages/core/pubspec.yaml +9 -0
- package/dart-src/packages/core/tool/dbg.dart +6 -0
- package/dart-src/plugins/aws/.gitkeep +0 -0
- package/dart-src/plugins/aws/lib/flutter_patcher_aws.dart +12 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +207 -0
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +130 -0
- package/dart-src/plugins/aws/lib/src/aws_config.dart +59 -0
- package/dart-src/plugins/aws/lib/src/aws_database.dart +198 -0
- package/dart-src/plugins/aws/lib/src/aws_lambda_edge_storage.dart +72 -0
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +312 -0
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +125 -0
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +20 -0
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +123 -0
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +136 -0
- package/dart-src/plugins/aws/pubspec.yaml +22 -0
- package/dart-src/plugins/cloudflare/.gitkeep +0 -0
- package/dart-src/plugins/cloudflare/lib/flutter_patcher_cloudflare.dart +18 -0
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +29 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +93 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +131 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_client.dart +66 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_config.dart +30 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database.dart +16 -0
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +314 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +50 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +287 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +23 -0
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +110 -0
- package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +16 -0
- package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +8 -0
- package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +38 -0
- package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +11 -0
- package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +24 -0
- package/dart-src/plugins/cloudflare/pubspec.yaml +20 -0
- package/dart-src/plugins/cloudflare/sql/bundles.sql +42 -0
- package/dart-src/plugins/plugin-core/lib/flutter_patcher_plugin_core.dart +42 -0
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +94 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +259 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work_store.dart +20 -0
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +29 -0
- package/dart-src/plugins/plugin-core/lib/src/compression_format.dart +63 -0
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +22 -0
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +853 -0
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +525 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_key_builder.dart +15 -0
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +130 -0
- package/dart-src/plugins/plugin-core/lib/src/filter_compatible_app_versions.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/generate_min_bundle_id.dart +19 -0
- package/dart-src/plugins/plugin-core/lib/src/get_update_info.dart +241 -0
- package/dart-src/plugins/plugin-core/lib/src/is_object.dart +5 -0
- package/dart-src/plugins/plugin-core/lib/src/legacy_asset_storage_layout.dart +9 -0
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +112 -0
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +44 -0
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +70 -0
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +62 -0
- package/dart-src/plugins/plugin-core/lib/src/resolve_update_info_from_bundles.dart +43 -0
- package/dart-src/plugins/plugin-core/lib/src/storage_profile.dart +27 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +454 -0
- package/dart-src/plugins/plugin-core/lib/src/uuidv7.dart +41 -0
- package/dart-src/plugins/plugin-core/pubspec.yaml +15 -0
- package/dart-src/plugins/postgres/.gitkeep +0 -0
- package/dart-src/plugins/postgres/lib/flutter_patcher_postgres.dart +17 -0
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +130 -0
- package/dart-src/plugins/postgres/lib/src/postgres_client.dart +90 -0
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +35 -0
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +302 -0
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +120 -0
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +205 -0
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +132 -0
- package/dart-src/plugins/postgres/pubspec.yaml +18 -0
- package/dart-src/plugins/postgres/sql/bundles.sql +45 -0
- package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +21 -0
- package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +171 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +126 -0
- package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +125 -0
- package/dart-src/plugins/postgres/sql/hash_user_id.sql +30 -0
- package/dart-src/plugins/postgres/sql/is_device_eligible.sql +237 -0
- package/dart-src/plugins/postgres/sql/prepareSql.ts +11 -0
- package/dart-src/plugins/standalone/.gitkeep +0 -0
- package/dart-src/plugins/standalone/lib/flutter_patcher_standalone.dart +12 -0
- package/dart-src/plugins/standalone/lib/src/standalone_config.dart +107 -0
- package/dart-src/plugins/standalone/lib/src/standalone_database.dart +233 -0
- package/dart-src/plugins/standalone/lib/src/standalone_storage.dart +175 -0
- package/dart-src/plugins/standalone/pubspec.yaml +18 -0
- package/dart-src/plugins/supabase/lib/edge.dart +10 -0
- package/dart-src/plugins/supabase/lib/flutter_patcher_supabase.dart +21 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +129 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +154 -0
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +516 -0
- package/dart-src/plugins/supabase/lib/src/supabase_config.dart +34 -0
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +422 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_database.dart +32 -0
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +99 -0
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +145 -0
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +298 -0
- package/dart-src/plugins/supabase/lib/src/types.dart +130 -0
- package/dart-src/plugins/supabase/pubspec.yaml +19 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +44 -0
- package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +968 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +228 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +134 -0
- package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +194 -0
- package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +184 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +503 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +50 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +20 -0
- package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +48 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +53 -25
- /package/bin/{flutter-patcher-linux → flutter-patcher-linux-x64} +0 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
2
|
+
import 'dart:io';
|
|
3
|
+
|
|
4
|
+
import 'package:flutter_patcher_aws/flutter_patcher_aws.dart';
|
|
5
|
+
import 'package:postgres/postgres.dart';
|
|
6
|
+
import 'package:flutter_patcher_cloudflare/flutter_patcher_cloudflare.dart';
|
|
7
|
+
import 'package:flutter_patcher_postgres/flutter_patcher_postgres.dart';
|
|
8
|
+
import 'package:flutter_patcher_standalone/flutter_patcher_standalone.dart';
|
|
9
|
+
import 'package:flutter_patcher_supabase/flutter_patcher_supabase.dart';
|
|
10
|
+
import 'package:path/path.dart' as p;
|
|
11
|
+
|
|
12
|
+
/// Resolved configuration for the flutter_patcher CLI.
|
|
13
|
+
///
|
|
14
|
+
/// Mirrors hot-updater's config precedence: explicit flags > environment
|
|
15
|
+
/// variables > the project `.flutter_patcher.json` (falling back to the global
|
|
16
|
+
/// `~/.flutter_patcher/config.json`).
|
|
17
|
+
class FlutterPatcherConfig {
|
|
18
|
+
FlutterPatcherConfig({
|
|
19
|
+
required this.provider,
|
|
20
|
+
required this.supabase,
|
|
21
|
+
this.postgres = const PostgresConfigJson(),
|
|
22
|
+
this.cloudflare = const CloudflareConfigJson(),
|
|
23
|
+
this.aws = const AwsConfigJson(),
|
|
24
|
+
this.standalone = const StandaloneConfigJson(),
|
|
25
|
+
this.channel = 'production',
|
|
26
|
+
this.platform = 'android',
|
|
27
|
+
this.source = './dist',
|
|
28
|
+
this.publicKey,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
factory FlutterPatcherConfig.fromJson(Map<String, dynamic> json) =>
|
|
32
|
+
FlutterPatcherConfig(
|
|
33
|
+
provider: json['provider'] as String? ?? 'supabase',
|
|
34
|
+
supabase: SupabaseConfigJson.fromJson(
|
|
35
|
+
(json['supabase'] as Map?)?.cast<String, dynamic>() ?? {},
|
|
36
|
+
),
|
|
37
|
+
postgres: PostgresConfigJson.fromJson(
|
|
38
|
+
(json['postgres'] as Map?)?.cast<String, dynamic>() ?? {},
|
|
39
|
+
),
|
|
40
|
+
cloudflare: CloudflareConfigJson.fromJson(
|
|
41
|
+
(json['cloudflare'] as Map?)?.cast<String, dynamic>() ?? {},
|
|
42
|
+
),
|
|
43
|
+
aws: AwsConfigJson.fromJson(
|
|
44
|
+
(json['aws'] as Map?)?.cast<String, dynamic>() ?? {},
|
|
45
|
+
),
|
|
46
|
+
standalone: StandaloneConfigJson.fromJson(
|
|
47
|
+
(json['standalone'] as Map?)?.cast<String, dynamic>() ?? {},
|
|
48
|
+
),
|
|
49
|
+
channel: json['channel'] as String? ?? 'production',
|
|
50
|
+
platform: json['platform'] as String? ?? 'android',
|
|
51
|
+
source: json['source'] as String? ?? './dist',
|
|
52
|
+
publicKey: json['publicKey'] as String?,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
final String provider;
|
|
56
|
+
final SupabaseConfigJson supabase;
|
|
57
|
+
final PostgresConfigJson postgres;
|
|
58
|
+
final CloudflareConfigJson cloudflare;
|
|
59
|
+
final AwsConfigJson aws;
|
|
60
|
+
final StandaloneConfigJson standalone;
|
|
61
|
+
final String channel;
|
|
62
|
+
final String platform;
|
|
63
|
+
final String source;
|
|
64
|
+
final String? publicKey;
|
|
65
|
+
|
|
66
|
+
Map<String, dynamic> toJson() => {
|
|
67
|
+
'provider': provider,
|
|
68
|
+
'supabase': supabase.toJson(),
|
|
69
|
+
'postgres': postgres.toJson(),
|
|
70
|
+
'cloudflare': cloudflare.toJson(),
|
|
71
|
+
'aws': aws.toJson(),
|
|
72
|
+
'standalone': standalone.toJson(),
|
|
73
|
+
'channel': channel,
|
|
74
|
+
'platform': platform,
|
|
75
|
+
'source': source,
|
|
76
|
+
if (publicKey != null) 'publicKey': publicKey,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
FlutterPatcherConfig copyWith({
|
|
80
|
+
String? provider,
|
|
81
|
+
SupabaseConfigJson? supabase,
|
|
82
|
+
PostgresConfigJson? postgres,
|
|
83
|
+
CloudflareConfigJson? cloudflare,
|
|
84
|
+
AwsConfigJson? aws,
|
|
85
|
+
StandaloneConfigJson? standalone,
|
|
86
|
+
String? channel,
|
|
87
|
+
String? platform,
|
|
88
|
+
String? source,
|
|
89
|
+
String? publicKey,
|
|
90
|
+
}) =>
|
|
91
|
+
FlutterPatcherConfig(
|
|
92
|
+
provider: provider ?? this.provider,
|
|
93
|
+
supabase: supabase ?? this.supabase,
|
|
94
|
+
postgres: postgres ?? this.postgres,
|
|
95
|
+
cloudflare: cloudflare ?? this.cloudflare,
|
|
96
|
+
aws: aws ?? this.aws,
|
|
97
|
+
standalone: standalone ?? this.standalone,
|
|
98
|
+
channel: channel ?? this.channel,
|
|
99
|
+
platform: platform ?? this.platform,
|
|
100
|
+
source: source ?? this.source,
|
|
101
|
+
publicKey: publicKey ?? this.publicKey,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Supabase section of [FlutterPatcherConfig].
|
|
106
|
+
class SupabaseConfigJson {
|
|
107
|
+
const SupabaseConfigJson({
|
|
108
|
+
this.url,
|
|
109
|
+
this.serviceRoleKey,
|
|
110
|
+
this.anonKey,
|
|
111
|
+
this.bucket,
|
|
112
|
+
this.basePath,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
factory SupabaseConfigJson.fromJson(Map<String, dynamic> json) =>
|
|
116
|
+
SupabaseConfigJson(
|
|
117
|
+
url: json['url'] as String?,
|
|
118
|
+
serviceRoleKey: json['serviceRoleKey'] as String?,
|
|
119
|
+
anonKey: json['anonKey'] as String?,
|
|
120
|
+
bucket: json['bucket'] as String?,
|
|
121
|
+
basePath: json['basePath'] as String?,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
final String? url;
|
|
125
|
+
final String? serviceRoleKey;
|
|
126
|
+
final String? anonKey;
|
|
127
|
+
final String? bucket;
|
|
128
|
+
final String? basePath;
|
|
129
|
+
|
|
130
|
+
Map<String, dynamic> toJson() => {
|
|
131
|
+
if (url != null) 'url': url,
|
|
132
|
+
if (serviceRoleKey != null) 'serviceRoleKey': serviceRoleKey,
|
|
133
|
+
if (anonKey != null) 'anonKey': anonKey,
|
|
134
|
+
if (bucket != null) 'bucket': bucket,
|
|
135
|
+
if (basePath != null) 'basePath': basePath,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// Search order for a config file on disk.
|
|
140
|
+
List<File> configCandidates() {
|
|
141
|
+
final cwd = File(p.join(Directory.current.path, '.flutter_patcher.json'));
|
|
142
|
+
final home = Platform.environment['HOME'] ??
|
|
143
|
+
Platform.environment['USERPROFILE'] ??
|
|
144
|
+
'';
|
|
145
|
+
final global = File(p.join(home, '.flutter_patcher', 'config.json'));
|
|
146
|
+
return [cwd, global];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// Load the first existing config (cwd first, then global). Returns `null` if
|
|
150
|
+
/// neither exists.
|
|
151
|
+
FlutterPatcherConfig? loadConfig() {
|
|
152
|
+
for (final file in configCandidates()) {
|
|
153
|
+
if (file.existsSync()) {
|
|
154
|
+
final raw = file.readAsStringSync();
|
|
155
|
+
if (raw.trim().isEmpty) continue;
|
|
156
|
+
return FlutterPatcherConfig.fromJson(
|
|
157
|
+
jsonDecode(raw) as Map<String, dynamic>,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Persist a config to the project file (cwd).
|
|
165
|
+
void saveConfig(FlutterPatcherConfig config) {
|
|
166
|
+
final file = configCandidates().first;
|
|
167
|
+
file.parent.createSync(recursive: true);
|
|
168
|
+
file.writeAsStringSync(
|
|
169
|
+
const JsonEncoder.withIndent(' ').convert(config.toJson()),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Nested dot-path get/set helpers used by `config get/set`.
|
|
174
|
+
Object? readPath(Map<String, dynamic> map, String path) {
|
|
175
|
+
final parts = path.split('.');
|
|
176
|
+
Object? current = map;
|
|
177
|
+
for (final part in parts) {
|
|
178
|
+
if (current is! Map) return null;
|
|
179
|
+
current = current[part];
|
|
180
|
+
}
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void writePath(Map<String, dynamic> map, String path, Object? value) {
|
|
185
|
+
final parts = path.split('.');
|
|
186
|
+
Map<String, dynamic> current = map;
|
|
187
|
+
for (var i = 0; i < parts.length - 1; i++) {
|
|
188
|
+
final part = parts[i];
|
|
189
|
+
final next = current[part];
|
|
190
|
+
if (next is Map<String, dynamic>) {
|
|
191
|
+
current = next;
|
|
192
|
+
} else {
|
|
193
|
+
current[part] = <String, dynamic>{};
|
|
194
|
+
current = current[part] as Map<String, dynamic>;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
current[parts.last] = value;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// Resolve Supabase credentials honoring flag > env > config precedence.
|
|
201
|
+
SupabaseServiceRoleConfig resolveSupabaseConfig(
|
|
202
|
+
FlutterPatcherConfig config, {
|
|
203
|
+
String? url,
|
|
204
|
+
String? serviceRoleKey,
|
|
205
|
+
String? anonKey,
|
|
206
|
+
String? bucket,
|
|
207
|
+
String? basePath,
|
|
208
|
+
SupabaseClientFactory? clientFactory,
|
|
209
|
+
}) {
|
|
210
|
+
final env = Platform.environment;
|
|
211
|
+
final resolvedUrl = url ?? env['SUPABASE_URL'] ?? config.supabase.url;
|
|
212
|
+
final resolvedService = serviceRoleKey ??
|
|
213
|
+
env['SUPABASE_SERVICE_ROLE_KEY'] ??
|
|
214
|
+
config.supabase.serviceRoleKey;
|
|
215
|
+
final resolvedAnon =
|
|
216
|
+
anonKey ?? env['SUPABASE_ANON_KEY'] ?? config.supabase.anonKey;
|
|
217
|
+
|
|
218
|
+
if (resolvedUrl == null || resolvedUrl.isEmpty) {
|
|
219
|
+
throw StateError(
|
|
220
|
+
'Supabase URL is required. Set it via --url, SUPABASE_URL, or '
|
|
221
|
+
'`flutter_patcher config set supabase.url <url>`.',
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return SupabaseServiceRoleConfig(
|
|
226
|
+
supabaseUrl: resolvedUrl,
|
|
227
|
+
supabaseServiceRoleKey: resolvedService,
|
|
228
|
+
supabaseAnonKey: resolvedAnon,
|
|
229
|
+
clientFactory: clientFactory,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// Resolve the Supabase storage config (mirrors [resolveSupabaseConfig]).
|
|
234
|
+
SupabaseStorageConfig resolveSupabaseStorageConfig(
|
|
235
|
+
FlutterPatcherConfig config, {
|
|
236
|
+
String? url,
|
|
237
|
+
String? serviceRoleKey,
|
|
238
|
+
String? anonKey,
|
|
239
|
+
String? bucket,
|
|
240
|
+
String? basePath,
|
|
241
|
+
SupabaseClientFactory? clientFactory,
|
|
242
|
+
}) {
|
|
243
|
+
final db = resolveSupabaseConfig(
|
|
244
|
+
config,
|
|
245
|
+
url: url,
|
|
246
|
+
serviceRoleKey: serviceRoleKey,
|
|
247
|
+
anonKey: anonKey,
|
|
248
|
+
bucket: bucket,
|
|
249
|
+
basePath: basePath,
|
|
250
|
+
clientFactory: clientFactory,
|
|
251
|
+
);
|
|
252
|
+
return SupabaseStorageConfig(
|
|
253
|
+
supabaseUrl: db.supabaseUrl,
|
|
254
|
+
supabaseServiceRoleKey: db.supabaseServiceRoleKey,
|
|
255
|
+
supabaseAnonKey: db.supabaseAnonKey,
|
|
256
|
+
clientFactory: db.clientFactory,
|
|
257
|
+
bucketName: db.supabaseServiceRoleKey != null || db.supabaseAnonKey != null
|
|
258
|
+
? (bucket ??
|
|
259
|
+
Platform.environment['SUPABASE_BUCKET'] ??
|
|
260
|
+
config.supabase.bucket ??
|
|
261
|
+
'bundles')
|
|
262
|
+
: (bucket ?? config.supabase.bucket ?? 'bundles'),
|
|
263
|
+
basePath: basePath ??
|
|
264
|
+
Platform.environment['SUPABASE_BASE_PATH'] ??
|
|
265
|
+
config.supabase.basePath,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Postgres backend config
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
/// Postgres section of [FlutterPatcherConfig].
|
|
274
|
+
class PostgresConfigJson {
|
|
275
|
+
const PostgresConfigJson({
|
|
276
|
+
this.host,
|
|
277
|
+
this.port,
|
|
278
|
+
this.database,
|
|
279
|
+
this.username,
|
|
280
|
+
this.password,
|
|
281
|
+
this.sslMode,
|
|
282
|
+
this.basePath,
|
|
283
|
+
this.servingBaseUrl,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
factory PostgresConfigJson.fromJson(Map<String, dynamic> json) =>
|
|
287
|
+
PostgresConfigJson(
|
|
288
|
+
host: json['host'] as String?,
|
|
289
|
+
port: json['port'] as String?,
|
|
290
|
+
database: json['database'] as String?,
|
|
291
|
+
username: json['username'] as String?,
|
|
292
|
+
password: json['password'] as String?,
|
|
293
|
+
sslMode: json['sslMode'] as String?,
|
|
294
|
+
basePath: json['basePath'] as String?,
|
|
295
|
+
servingBaseUrl: json['servingBaseUrl'] as String?,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
final String? host;
|
|
299
|
+
final String? port;
|
|
300
|
+
final String? database;
|
|
301
|
+
final String? username;
|
|
302
|
+
final String? password;
|
|
303
|
+
final String? sslMode;
|
|
304
|
+
final String? basePath;
|
|
305
|
+
final String? servingBaseUrl;
|
|
306
|
+
|
|
307
|
+
Map<String, dynamic> toJson() => {
|
|
308
|
+
if (host != null) 'host': host,
|
|
309
|
+
if (port != null) 'port': port,
|
|
310
|
+
if (database != null) 'database': database,
|
|
311
|
+
if (username != null) 'username': username,
|
|
312
|
+
if (password != null) 'password': password,
|
|
313
|
+
if (sslMode != null) 'sslMode': sslMode,
|
|
314
|
+
if (basePath != null) 'basePath': basePath,
|
|
315
|
+
if (servingBaseUrl != null) 'servingBaseUrl': servingBaseUrl,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
SslMode? _parseSslMode(String? value) {
|
|
320
|
+
switch (value?.toLowerCase()) {
|
|
321
|
+
case 'disable':
|
|
322
|
+
return SslMode.disable;
|
|
323
|
+
case 'require':
|
|
324
|
+
return SslMode.require;
|
|
325
|
+
case 'verify-ca':
|
|
326
|
+
case 'verify-full':
|
|
327
|
+
return SslMode.verifyFull;
|
|
328
|
+
default:
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/// Resolve the Postgres database config (flag > env > json precedence).
|
|
334
|
+
PostgresConfig resolvePostgresDatabaseConfig(
|
|
335
|
+
FlutterPatcherConfig config, {
|
|
336
|
+
String? host,
|
|
337
|
+
String? port,
|
|
338
|
+
String? database,
|
|
339
|
+
String? username,
|
|
340
|
+
String? password,
|
|
341
|
+
String? sslMode,
|
|
342
|
+
PostgresClientFactory? clientFactory,
|
|
343
|
+
}) {
|
|
344
|
+
final env = Platform.environment;
|
|
345
|
+
final resolvedHost = host ?? env['POSTGRES_HOST'] ?? config.postgres.host;
|
|
346
|
+
final resolvedDatabase =
|
|
347
|
+
database ?? env['POSTGRES_DB'] ?? config.postgres.database;
|
|
348
|
+
final resolvedUser = username ?? env['POSTGRES_USER'] ?? config.postgres.username;
|
|
349
|
+
final resolvedPassword =
|
|
350
|
+
password ?? env['POSTGRES_PASSWORD'] ?? config.postgres.password;
|
|
351
|
+
final resolvedPort = port ??
|
|
352
|
+
env['POSTGRES_PORT'] ??
|
|
353
|
+
config.postgres.port ??
|
|
354
|
+
'5432';
|
|
355
|
+
final resolvedSsl =
|
|
356
|
+
sslMode ?? env['POSTGRES_SSLMODE'] ?? config.postgres.sslMode;
|
|
357
|
+
|
|
358
|
+
if (resolvedHost == null || resolvedHost.isEmpty) {
|
|
359
|
+
throw StateError(
|
|
360
|
+
'Postgres host is required. Set --pg-host, POSTGRES_HOST, or '
|
|
361
|
+
'`flutter_patcher config set postgres.host <host>`.',
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return PostgresConfig(
|
|
366
|
+
host: resolvedHost,
|
|
367
|
+
port: int.tryParse(resolvedPort) ?? 5432,
|
|
368
|
+
database: resolvedDatabase ?? 'postgres',
|
|
369
|
+
username: resolvedUser,
|
|
370
|
+
password: resolvedPassword,
|
|
371
|
+
sslMode: _parseSslMode(resolvedSsl),
|
|
372
|
+
clientFactory: clientFactory,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/// Resolve the Postgres storage config (shares the DB connection).
|
|
377
|
+
PostgresStorageConfig resolvePostgresStorageConfig(
|
|
378
|
+
FlutterPatcherConfig config, {
|
|
379
|
+
String? host,
|
|
380
|
+
String? port,
|
|
381
|
+
String? database,
|
|
382
|
+
String? username,
|
|
383
|
+
String? password,
|
|
384
|
+
String? sslMode,
|
|
385
|
+
String? basePath,
|
|
386
|
+
String? servingBaseUrl,
|
|
387
|
+
PostgresClientFactory? clientFactory,
|
|
388
|
+
}) {
|
|
389
|
+
final db = resolvePostgresDatabaseConfig(
|
|
390
|
+
config,
|
|
391
|
+
host: host,
|
|
392
|
+
port: port,
|
|
393
|
+
database: database,
|
|
394
|
+
username: username,
|
|
395
|
+
password: password,
|
|
396
|
+
sslMode: sslMode,
|
|
397
|
+
clientFactory: clientFactory,
|
|
398
|
+
);
|
|
399
|
+
return PostgresStorageConfig(
|
|
400
|
+
db: db,
|
|
401
|
+
basePath: basePath ??
|
|
402
|
+
Platform.environment['POSTGRES_BASE_PATH'] ??
|
|
403
|
+
config.postgres.basePath,
|
|
404
|
+
servingBaseUrl: servingBaseUrl ??
|
|
405
|
+
Platform.environment['POSTGRES_SERVING_BASE_URL'] ??
|
|
406
|
+
config.postgres.servingBaseUrl,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
// Cloudflare (D1 + R2) backend config
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
/// Cloudflare section of [FlutterPatcherConfig].
|
|
415
|
+
class CloudflareConfigJson {
|
|
416
|
+
const CloudflareConfigJson({
|
|
417
|
+
this.accountId,
|
|
418
|
+
this.d1DatabaseId,
|
|
419
|
+
this.apiToken,
|
|
420
|
+
this.r2Bucket,
|
|
421
|
+
this.r2AccessKeyId,
|
|
422
|
+
this.r2SecretAccessKey,
|
|
423
|
+
this.r2BasePath,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
factory CloudflareConfigJson.fromJson(Map<String, dynamic> json) =>
|
|
427
|
+
CloudflareConfigJson(
|
|
428
|
+
accountId: json['accountId'] as String?,
|
|
429
|
+
d1DatabaseId: json['d1DatabaseId'] as String?,
|
|
430
|
+
apiToken: json['apiToken'] as String?,
|
|
431
|
+
r2Bucket: json['r2Bucket'] as String?,
|
|
432
|
+
r2AccessKeyId: json['r2AccessKeyId'] as String?,
|
|
433
|
+
r2SecretAccessKey: json['r2SecretAccessKey'] as String?,
|
|
434
|
+
r2BasePath: json['r2BasePath'] as String?,
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
final String? accountId;
|
|
438
|
+
final String? d1DatabaseId;
|
|
439
|
+
final String? apiToken;
|
|
440
|
+
final String? r2Bucket;
|
|
441
|
+
final String? r2AccessKeyId;
|
|
442
|
+
final String? r2SecretAccessKey;
|
|
443
|
+
final String? r2BasePath;
|
|
444
|
+
|
|
445
|
+
Map<String, dynamic> toJson() => {
|
|
446
|
+
if (accountId != null) 'accountId': accountId,
|
|
447
|
+
if (d1DatabaseId != null) 'd1DatabaseId': d1DatabaseId,
|
|
448
|
+
if (apiToken != null) 'apiToken': apiToken,
|
|
449
|
+
if (r2Bucket != null) 'r2Bucket': r2Bucket,
|
|
450
|
+
if (r2AccessKeyId != null) 'r2AccessKeyId': r2AccessKeyId,
|
|
451
|
+
if (r2SecretAccessKey != null) 'r2SecretAccessKey': r2SecretAccessKey,
|
|
452
|
+
if (r2BasePath != null) 'r2BasePath': r2BasePath,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/// Resolve the Cloudflare D1 database config.
|
|
457
|
+
D1DatabaseConfig resolveCloudflareDatabaseConfig(
|
|
458
|
+
FlutterPatcherConfig config, {
|
|
459
|
+
String? accountId,
|
|
460
|
+
String? d1DatabaseId,
|
|
461
|
+
String? apiToken,
|
|
462
|
+
D1ClientFactory? clientFactory,
|
|
463
|
+
}) {
|
|
464
|
+
final env = Platform.environment;
|
|
465
|
+
final resolvedAccount =
|
|
466
|
+
accountId ?? env['CLOUDFLARE_ACCOUNT_ID'] ?? config.cloudflare.accountId;
|
|
467
|
+
final resolvedDb = d1DatabaseId ??
|
|
468
|
+
env['CLOUDFLARE_D1_DATABASE_ID'] ??
|
|
469
|
+
config.cloudflare.d1DatabaseId;
|
|
470
|
+
final resolvedToken =
|
|
471
|
+
apiToken ?? env['CLOUDFLARE_API_TOKEN'] ?? config.cloudflare.apiToken;
|
|
472
|
+
|
|
473
|
+
if (resolvedAccount == null ||
|
|
474
|
+
resolvedDb == null ||
|
|
475
|
+
resolvedToken == null) {
|
|
476
|
+
throw StateError(
|
|
477
|
+
'Cloudflare requires accountId, d1DatabaseId and apiToken. Set them via '
|
|
478
|
+
'flags, CLOUDFLARE_* env vars, or `flutter_patcher config set cloudflare.*`.',
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return D1DatabaseConfig(
|
|
483
|
+
accountId: resolvedAccount,
|
|
484
|
+
databaseId: resolvedDb,
|
|
485
|
+
cloudflareApiToken: resolvedToken,
|
|
486
|
+
clientFactory: clientFactory,
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/// Resolve the Cloudflare R2 storage config.
|
|
491
|
+
R2S3StorageConfig resolveCloudflareStorageConfig(
|
|
492
|
+
FlutterPatcherConfig config, {
|
|
493
|
+
String? accountId,
|
|
494
|
+
String? bucket,
|
|
495
|
+
String? accessKeyId,
|
|
496
|
+
String? secretAccessKey,
|
|
497
|
+
String? basePath,
|
|
498
|
+
R2S3ClientFactory? clientFactory,
|
|
499
|
+
}) {
|
|
500
|
+
final env = Platform.environment;
|
|
501
|
+
final resolvedAccount = accountId ??
|
|
502
|
+
env['CLOUDFLARE_ACCOUNT_ID'] ??
|
|
503
|
+
env['R2_ACCOUNT_ID'] ??
|
|
504
|
+
config.cloudflare.accountId;
|
|
505
|
+
final resolvedBucket =
|
|
506
|
+
bucket ?? env['R2_BUCKET'] ?? config.cloudflare.r2Bucket ?? 'bundles';
|
|
507
|
+
final resolvedKey = accessKeyId ??
|
|
508
|
+
env['R2_ACCESS_KEY_ID'] ??
|
|
509
|
+
config.cloudflare.r2AccessKeyId;
|
|
510
|
+
final resolvedSecret = secretAccessKey ??
|
|
511
|
+
env['R2_SECRET_ACCESS_KEY'] ??
|
|
512
|
+
config.cloudflare.r2SecretAccessKey;
|
|
513
|
+
|
|
514
|
+
if (resolvedAccount == null ||
|
|
515
|
+
resolvedKey == null ||
|
|
516
|
+
resolvedSecret == null) {
|
|
517
|
+
throw StateError(
|
|
518
|
+
'Cloudflare R2 requires accountId, accessKeyId and secretAccessKey. Set '
|
|
519
|
+
'them via flags, R2_* env vars, or `flutter_patcher config set cloudflare.*`.',
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return R2S3StorageConfig(
|
|
524
|
+
accountId: resolvedAccount,
|
|
525
|
+
bucketName: resolvedBucket,
|
|
526
|
+
accessKeyId: resolvedKey,
|
|
527
|
+
secretAccessKey: resolvedSecret,
|
|
528
|
+
basePath: basePath ?? env['R2_BASE_PATH'] ?? config.cloudflare.r2BasePath,
|
|
529
|
+
clientFactory: clientFactory,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ---------------------------------------------------------------------------
|
|
534
|
+
// AWS (S3 DB + S3 storage) backend config
|
|
535
|
+
// ---------------------------------------------------------------------------
|
|
536
|
+
|
|
537
|
+
/// AWS section of [FlutterPatcherConfig].
|
|
538
|
+
class AwsConfigJson {
|
|
539
|
+
const AwsConfigJson({
|
|
540
|
+
this.bucket,
|
|
541
|
+
this.region,
|
|
542
|
+
this.accessKeyId,
|
|
543
|
+
this.secretAccessKey,
|
|
544
|
+
this.endpoint,
|
|
545
|
+
this.basePath,
|
|
546
|
+
this.sessionToken,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
factory AwsConfigJson.fromJson(Map<String, dynamic> json) => AwsConfigJson(
|
|
550
|
+
bucket: json['bucket'] as String?,
|
|
551
|
+
region: json['region'] as String?,
|
|
552
|
+
accessKeyId: json['accessKeyId'] as String?,
|
|
553
|
+
secretAccessKey: json['secretAccessKey'] as String?,
|
|
554
|
+
endpoint: json['endpoint'] as String?,
|
|
555
|
+
basePath: json['basePath'] as String?,
|
|
556
|
+
sessionToken: json['sessionToken'] as String?,
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
final String? bucket;
|
|
560
|
+
final String? region;
|
|
561
|
+
final String? accessKeyId;
|
|
562
|
+
final String? secretAccessKey;
|
|
563
|
+
final String? endpoint;
|
|
564
|
+
final String? basePath;
|
|
565
|
+
final String? sessionToken;
|
|
566
|
+
|
|
567
|
+
Map<String, dynamic> toJson() => {
|
|
568
|
+
if (bucket != null) 'bucket': bucket,
|
|
569
|
+
if (region != null) 'region': region,
|
|
570
|
+
if (accessKeyId != null) 'accessKeyId': accessKeyId,
|
|
571
|
+
if (secretAccessKey != null) 'secretAccessKey': secretAccessKey,
|
|
572
|
+
if (endpoint != null) 'endpoint': endpoint,
|
|
573
|
+
if (basePath != null) 'basePath': basePath,
|
|
574
|
+
if (sessionToken != null) 'sessionToken': sessionToken,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/// Resolve the AWS S3 database config.
|
|
579
|
+
S3DatabaseConfig resolveAwsDatabaseConfig(
|
|
580
|
+
FlutterPatcherConfig config, {
|
|
581
|
+
String? bucket,
|
|
582
|
+
String? region,
|
|
583
|
+
String? accessKeyId,
|
|
584
|
+
String? secretAccessKey,
|
|
585
|
+
String? basePath,
|
|
586
|
+
String? endpoint,
|
|
587
|
+
String? sessionToken,
|
|
588
|
+
AwsS3ClientLike Function(S3DatabaseConfig config)? clientFactory,
|
|
589
|
+
AwsCloudFrontClientLike Function(S3DatabaseConfig config)? cloudfrontClientFactory,
|
|
590
|
+
}) {
|
|
591
|
+
final env = Platform.environment;
|
|
592
|
+
final resolvedBucket = bucket ?? env['AWS_BUCKET'] ?? config.aws.bucket;
|
|
593
|
+
final resolvedRegion = region ?? env['AWS_REGION'] ?? config.aws.region;
|
|
594
|
+
final resolvedKey =
|
|
595
|
+
accessKeyId ?? env['AWS_ACCESS_KEY_ID'] ?? config.aws.accessKeyId;
|
|
596
|
+
final resolvedSecret =
|
|
597
|
+
secretAccessKey ?? env['AWS_SECRET_ACCESS_KEY'] ?? config.aws.secretAccessKey;
|
|
598
|
+
|
|
599
|
+
if (resolvedBucket == null ||
|
|
600
|
+
resolvedRegion == null ||
|
|
601
|
+
resolvedKey == null ||
|
|
602
|
+
resolvedSecret == null) {
|
|
603
|
+
throw StateError(
|
|
604
|
+
'AWS requires bucket, region, accessKeyId and secretAccessKey. Set them '
|
|
605
|
+
'via flags, AWS_* env vars, or `flutter_patcher config set aws.*`.',
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return S3DatabaseConfig(
|
|
610
|
+
bucketName: resolvedBucket,
|
|
611
|
+
region: resolvedRegion,
|
|
612
|
+
accessKeyId: resolvedKey,
|
|
613
|
+
secretAccessKey: resolvedSecret,
|
|
614
|
+
basePath: basePath ?? env['AWS_BASE_PATH'] ?? config.aws.basePath,
|
|
615
|
+
endpoint: endpoint ?? env['AWS_ENDPOINT'] ?? config.aws.endpoint,
|
|
616
|
+
sessionToken: sessionToken ?? env['AWS_SESSION_TOKEN'] ?? config.aws.sessionToken,
|
|
617
|
+
clientFactory: clientFactory ?? defaultAwsS3DatabaseClientFactory,
|
|
618
|
+
cloudfrontClientFactory:
|
|
619
|
+
cloudfrontClientFactory ?? defaultAwsCloudFrontClientFactory,
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/// Resolve the AWS S3 storage config.
|
|
624
|
+
AwsS3StorageConfig resolveAwsStorageConfig(
|
|
625
|
+
FlutterPatcherConfig config, {
|
|
626
|
+
String? bucket,
|
|
627
|
+
String? region,
|
|
628
|
+
String? accessKeyId,
|
|
629
|
+
String? secretAccessKey,
|
|
630
|
+
String? basePath,
|
|
631
|
+
String? endpoint,
|
|
632
|
+
String? sessionToken,
|
|
633
|
+
AwsS3ClientLike Function(AwsS3StorageConfig config)? clientFactory,
|
|
634
|
+
}) {
|
|
635
|
+
final env = Platform.environment;
|
|
636
|
+
final resolvedBucket = bucket ?? env['AWS_BUCKET'] ?? config.aws.bucket;
|
|
637
|
+
final resolvedRegion = region ?? env['AWS_REGION'] ?? config.aws.region;
|
|
638
|
+
final resolvedKey =
|
|
639
|
+
accessKeyId ?? env['AWS_ACCESS_KEY_ID'] ?? config.aws.accessKeyId;
|
|
640
|
+
final resolvedSecret =
|
|
641
|
+
secretAccessKey ?? env['AWS_SECRET_ACCESS_KEY'] ?? config.aws.secretAccessKey;
|
|
642
|
+
|
|
643
|
+
if (resolvedBucket == null ||
|
|
644
|
+
resolvedRegion == null ||
|
|
645
|
+
resolvedKey == null ||
|
|
646
|
+
resolvedSecret == null) {
|
|
647
|
+
throw StateError(
|
|
648
|
+
'AWS requires bucket, region, accessKeyId and secretAccessKey. Set them '
|
|
649
|
+
'via flags, AWS_* env vars, or `flutter_patcher config set aws.*`.',
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
return AwsS3StorageConfig(
|
|
654
|
+
bucketName: resolvedBucket,
|
|
655
|
+
region: resolvedRegion,
|
|
656
|
+
accessKeyId: resolvedKey,
|
|
657
|
+
secretAccessKey: resolvedSecret,
|
|
658
|
+
basePath: basePath ?? env['AWS_BASE_PATH'] ?? config.aws.basePath,
|
|
659
|
+
endpoint: endpoint ?? env['AWS_ENDPOINT'] ?? config.aws.endpoint,
|
|
660
|
+
sessionToken: sessionToken ?? env['AWS_SESSION_TOKEN'] ?? config.aws.sessionToken,
|
|
661
|
+
clientFactory: clientFactory ?? defaultAwsS3ClientFactory,
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/// Standalone (self-hosted) section of [FlutterPatcherConfig].
|
|
666
|
+
class StandaloneConfigJson {
|
|
667
|
+
const StandaloneConfigJson({this.baseUrl});
|
|
668
|
+
|
|
669
|
+
factory StandaloneConfigJson.fromJson(Map<String, dynamic> json) =>
|
|
670
|
+
StandaloneConfigJson(baseUrl: json['baseUrl'] as String?);
|
|
671
|
+
|
|
672
|
+
final String? baseUrl;
|
|
673
|
+
|
|
674
|
+
Map<String, dynamic> toJson() =>
|
|
675
|
+
{if (baseUrl != null) 'baseUrl': baseUrl};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/// Resolve the standalone repository (database) config.
|
|
679
|
+
StandaloneRepositoryConfig resolveStandaloneDatabaseConfig(
|
|
680
|
+
FlutterPatcherConfig config, {
|
|
681
|
+
String? baseUrl,
|
|
682
|
+
StandaloneClientFactory? clientFactory,
|
|
683
|
+
}) {
|
|
684
|
+
final env = Platform.environment;
|
|
685
|
+
final resolvedBaseUrl =
|
|
686
|
+
baseUrl ?? env['STANDALONE_BASE_URL'] ?? config.standalone.baseUrl;
|
|
687
|
+
if (resolvedBaseUrl == null || resolvedBaseUrl.isEmpty) {
|
|
688
|
+
throw StateError(
|
|
689
|
+
'Standalone baseUrl is required. Set --base-url, STANDALONE_BASE_URL, '
|
|
690
|
+
'or `flutter_patcher config set standalone.baseUrl <url>`.',
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
return StandaloneRepositoryConfig(
|
|
694
|
+
baseUrl: resolvedBaseUrl,
|
|
695
|
+
clientFactory: clientFactory,
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/// Resolve the standalone storage config (shares the same self-hosted base).
|
|
700
|
+
StandaloneStorageConfig resolveStandaloneStorageConfig(
|
|
701
|
+
FlutterPatcherConfig config, {
|
|
702
|
+
String? baseUrl,
|
|
703
|
+
StandaloneClientFactory? clientFactory,
|
|
704
|
+
}) {
|
|
705
|
+
final db = resolveStandaloneDatabaseConfig(
|
|
706
|
+
config,
|
|
707
|
+
baseUrl: baseUrl,
|
|
708
|
+
clientFactory: clientFactory,
|
|
709
|
+
);
|
|
710
|
+
return StandaloneStorageConfig(
|
|
711
|
+
baseUrl: db.baseUrl,
|
|
712
|
+
commonHeaders: db.commonHeaders,
|
|
713
|
+
clientFactory: clientFactory,
|
|
714
|
+
);
|
|
715
|
+
}
|