Import IKAR messenger client and server
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
final version = args.isEmpty ? '1.0.0' : args[0];
|
||||
final code = args.length < 2 ? 1 : int.parse(args[1]);
|
||||
final fileName = 'ikar-$version+$code.apk';
|
||||
final reportSuffix = code == 1 ? '' : '-$version';
|
||||
final base = Uri.parse('https://argus.kusoft.xyz');
|
||||
final http = HttpClient()..connectionTimeout = const Duration(seconds: 15);
|
||||
final local = File('artifacts/$fileName');
|
||||
final downloaded = File('artifacts/ikar-$version+$code.argus.apk');
|
||||
try {
|
||||
Future<dynamic> getJson(String path) async {
|
||||
final response = await (await http.getUrl(
|
||||
base.resolve(path),
|
||||
)).close().timeout(const Duration(seconds: 20));
|
||||
if (response.statusCode != 200) {
|
||||
throw StateError('HTTP ${response.statusCode} for $path');
|
||||
}
|
||||
return jsonDecode(
|
||||
await utf8.decodeStream(response).timeout(const Duration(seconds: 20)),
|
||||
);
|
||||
}
|
||||
|
||||
final results = await Future.wait([
|
||||
getJson('/api/apps/ikar/manifest?platform=android&channel=stable'),
|
||||
getJson('/api/apps'),
|
||||
getJson('/health'),
|
||||
]);
|
||||
final manifest = results[0] as Map<String, dynamic>;
|
||||
final release = manifest['release'] as Map<String, dynamic>;
|
||||
if (manifest['slug'] != 'ikar' ||
|
||||
manifest['name'] != 'ИКАР' ||
|
||||
release['version'] != version ||
|
||||
release['androidVersionCode'] != code ||
|
||||
release['platform'] != 'android' ||
|
||||
release['channel'] != 'stable' ||
|
||||
release['packageKind'] != 'apk' ||
|
||||
release['originalFileName'] != fileName) {
|
||||
throw StateError('Incorrect public application or release identity');
|
||||
}
|
||||
final catalog = results[1] as List;
|
||||
final listed = catalog.singleWhere((app) => app['slug'] == 'ikar');
|
||||
if (listed['name'] != 'ИКАР' ||
|
||||
listed['latestRelease']['id'] != release['id'] ||
|
||||
(results[2] as Map)['status'] != 'ok') {
|
||||
throw StateError('Catalog or service health verification failed');
|
||||
}
|
||||
final uri = base.resolve(release['downloadPath'] as String);
|
||||
if (uri.scheme != 'https' ||
|
||||
uri.host != base.host ||
|
||||
!uri.path.startsWith('/api/apps/ikar/releases/')) {
|
||||
throw StateError('Unexpected public download address');
|
||||
}
|
||||
final response = await (await http.getUrl(
|
||||
uri,
|
||||
)).close().timeout(const Duration(seconds: 20));
|
||||
if (response.statusCode != 200 ||
|
||||
response.headers.contentType?.mimeType !=
|
||||
'application/vnd.android.package-archive') {
|
||||
throw StateError('APK download unavailable');
|
||||
}
|
||||
await response
|
||||
.timeout(const Duration(seconds: 30))
|
||||
.pipe(downloaded.openWrite());
|
||||
final localDigest = (await sha256.bind(local.openRead()).first).toString();
|
||||
final publicDigest = (await sha256.bind(downloaded.openRead()).first)
|
||||
.toString();
|
||||
final size = await downloaded.length();
|
||||
if (publicDigest != localDigest ||
|
||||
publicDigest != release['sha256'] ||
|
||||
size != await local.length() ||
|
||||
size != release['packageSizeBytes']) {
|
||||
throw StateError('Public APK differs from the verified local artifact');
|
||||
}
|
||||
final report = {
|
||||
'passed': true,
|
||||
'slug': 'ikar',
|
||||
'name': 'ИКАР',
|
||||
'package': 'ru.ikar.app',
|
||||
'version': version,
|
||||
'androidVersionCode': code,
|
||||
'releaseId': release['id'],
|
||||
'sha256': publicDigest,
|
||||
'bytes': size,
|
||||
'tlsValidation': true,
|
||||
'listedInCatalog': true,
|
||||
'serviceHealth': 'ok',
|
||||
'cardUrl': 'https://argus.kusoft.xyz/#app=ikar',
|
||||
'downloadUrl': uri.toString(),
|
||||
'timestamp': DateTime.now().toUtc().toIso8601String(),
|
||||
};
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
await File(
|
||||
'artifacts/argus-manifest$reportSuffix.json',
|
||||
).writeAsString(encoder.convert(manifest));
|
||||
await File(
|
||||
'artifacts/argus-public-verification$reportSuffix.json',
|
||||
).writeAsString(encoder.convert(report));
|
||||
stdout.writeln(jsonEncode(report));
|
||||
} finally {
|
||||
http.close(force: true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user