feat: api2.oneme.ru с корнем Минцифры; kolibri с pub.dev вместо сабмодуля
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
name: Setup Rust
|
||||
description: >
|
||||
Install the Rust toolchain with platform targets and a build cache, so
|
||||
cargokit can compile the kolibri native library during the Flutter build.
|
||||
cargokit can compile the native libraries during the Flutter build. kolibri
|
||||
comes from pub.dev and builds inside the pub cache, so only the in-repo crate
|
||||
is listed here; the shared cargo registry and git checkouts are cached anyway.
|
||||
|
||||
inputs:
|
||||
targets:
|
||||
@@ -19,4 +21,4 @@ runs:
|
||||
- name: Cache Rust build
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: third_party/kolibri/kolibri-dart/rust
|
||||
workspaces: native/komet_crypto/rust
|
||||
|
||||
@@ -148,3 +148,8 @@ maxmint/
|
||||
maxtun/
|
||||
turnprobe/
|
||||
test/live_server_probe_test.dart
|
||||
|
||||
# local kolibri development: point the plugin at third_party/kolibri and the
|
||||
# Rust core at third_party/kolibri/kolibri-net instead of the published ones
|
||||
pubspec_overrides.yaml
|
||||
.cargo/
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
[submodule "third_party/rlottie"]
|
||||
path = third_party/rlottie
|
||||
url = https://github.com/Samsung/rlottie.git
|
||||
[submodule "third_party/kolibri"]
|
||||
path = third_party/kolibri
|
||||
url = https://github.com/KometTeam/kolibri.git
|
||||
|
||||
@@ -121,7 +121,7 @@ class Api {
|
||||
}
|
||||
if (gen != _connectGen) return;
|
||||
|
||||
({String host, int port}) endpoint;
|
||||
({String host, int port, bool trustMincifryCa}) endpoint;
|
||||
try {
|
||||
endpoint = await ServerConfig.loadEndpoint().timeout(_endpointTimeout);
|
||||
} catch (e) {
|
||||
@@ -129,10 +129,13 @@ class Api {
|
||||
endpoint = (
|
||||
host: ServerConfig.defaultHost,
|
||||
port: ServerConfig.defaultPort,
|
||||
trustMincifryCa: ServerConfig.defaultTrustMincifryCa,
|
||||
);
|
||||
}
|
||||
if (gen != _connectGen) return;
|
||||
|
||||
setTrustMincifryCa(enabled: endpoint.trustMincifryCa);
|
||||
|
||||
final (session, wireLog) = await _buildSessionOptions(endpoint);
|
||||
if (gen != _connectGen) return;
|
||||
|
||||
@@ -366,7 +369,7 @@ class Api {
|
||||
/// Строит устройство-поля и создаёт сессию ядра. Заодно заполняет
|
||||
/// [_userAgent] и [_deviceId] для геттеров.
|
||||
Future<(KolibriSession, Stream<WireLogEvent>)> _buildSessionOptions(
|
||||
({String host, int port}) endpoint,
|
||||
({String host, int port, bool trustMincifryCa}) endpoint,
|
||||
) async {
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class ServerConfig {
|
||||
static const String defaultHost = 'api.oneme.ru';
|
||||
static const String defaultHost = 'api2.oneme.ru';
|
||||
static const int defaultPort = 443;
|
||||
static const bool defaultTrustMincifryCa = true;
|
||||
static const String prefHostKey = 'server_host_override';
|
||||
static const String prefPortKey = 'server_port_override';
|
||||
static const String prefTrustMincifryKey = 'server_trust_mincifry_ca';
|
||||
static const Duration pingInterval = Duration(seconds: 10);
|
||||
static const Duration requestTimeout = Duration(seconds: 30);
|
||||
static const int maxReconnectAttempts = 50;
|
||||
|
||||
static Future<({String host, int port})> loadEndpoint() async {
|
||||
static Future<({String host, int port, bool trustMincifryCa})>
|
||||
loadEndpoint() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final rawHost = prefs.getString(prefHostKey);
|
||||
final rawPort = prefs.getInt(prefPortKey);
|
||||
@@ -20,6 +23,11 @@ abstract class ServerConfig {
|
||||
if (rawPort != null && rawPort >= 1 && rawPort <= 65535) {
|
||||
port = rawPort;
|
||||
}
|
||||
return (host: host, port: port);
|
||||
return (
|
||||
host: host,
|
||||
port: port,
|
||||
trustMincifryCa:
|
||||
prefs.getBool(prefTrustMincifryKey) ?? defaultTrustMincifryCa,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import '../calls/ws2_signaling.dart';
|
||||
import '../protocol/opcode_map.dart';
|
||||
import '../storage/app_instance.dart';
|
||||
import '../storage/token_storage.dart';
|
||||
import '../transport/tls_config.dart';
|
||||
import '../utils/logger.dart';
|
||||
|
||||
const _channelId = 'komet_messages';
|
||||
@@ -56,6 +57,7 @@ Future<void> _handleCallDecline(String payloadJson) async {
|
||||
|
||||
// Фоновый изолят: инициализируем ядро перед vcp-декодом/сигналингом.
|
||||
await initKolibri();
|
||||
await TlsConfig.applyMincifryTrust();
|
||||
|
||||
final params = ConversationParams.decode(vcp);
|
||||
if (params == null) return;
|
||||
@@ -93,6 +95,7 @@ Future<void> _handleReply(String payloadJson, String text) async {
|
||||
SharedPreferences.setPrefix('flutter.${AppInstance.id}.');
|
||||
} catch (_) {}
|
||||
}
|
||||
await TlsConfig.applyMincifryTrust();
|
||||
|
||||
final plugin = FlutterLocalNotificationsPlugin();
|
||||
final notifId = chatId & 0x7fffffff;
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
import 'package:kolibri/kolibri.dart' show setTrustMincifryCa;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../config/config.dart';
|
||||
|
||||
abstract class TlsConfig {
|
||||
static const String prefKey = 'dev_tls_insecure';
|
||||
|
||||
static Future<void> applyMincifryTrust() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
setTrustMincifryCa(
|
||||
enabled:
|
||||
prefs.getBool(ServerConfig.prefTrustMincifryKey) ??
|
||||
ServerConfig.defaultTrustMincifryCa,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<bool> isInsecureAllowed() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getBool(prefKey) ?? false;
|
||||
|
||||
@@ -27,6 +27,7 @@ class _ServerSettingsSheetState extends State<ServerSettingsSheet> {
|
||||
text: '${ServerConfig.defaultPort}',
|
||||
);
|
||||
bool _busy = false;
|
||||
bool _trustMincifryCa = ServerConfig.defaultTrustMincifryCa;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -40,6 +41,7 @@ class _ServerSettingsSheetState extends State<ServerSettingsSheet> {
|
||||
setState(() {
|
||||
_hostController.text = endpoint.host;
|
||||
_portController.text = '${endpoint.port}';
|
||||
_trustMincifryCa = endpoint.trustMincifryCa;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,6 +57,7 @@ class _ServerSettingsSheetState extends State<ServerSettingsSheet> {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(ServerConfig.prefHostKey, host);
|
||||
await prefs.setInt(ServerConfig.prefPortKey, port);
|
||||
await prefs.setBool(ServerConfig.prefTrustMincifryKey, _trustMincifryCa);
|
||||
await api.disconnect();
|
||||
unawaited(api.connect());
|
||||
final online = await api.stateStream
|
||||
@@ -82,8 +85,10 @@ class _ServerSettingsSheetState extends State<ServerSettingsSheet> {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(ServerConfig.prefHostKey);
|
||||
await prefs.remove(ServerConfig.prefPortKey);
|
||||
await prefs.remove(ServerConfig.prefTrustMincifryKey);
|
||||
_hostController.text = ServerConfig.defaultHost;
|
||||
_portController.text = '${ServerConfig.defaultPort}';
|
||||
_trustMincifryCa = ServerConfig.defaultTrustMincifryCa;
|
||||
await api.disconnect();
|
||||
api.connect();
|
||||
final online = await api.stateStream
|
||||
@@ -154,6 +159,49 @@ class _ServerSettingsSheetState extends State<ServerSettingsSheet> {
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
enabled: !_busy,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.fromLTRB(14, 12, 10, 12),
|
||||
decoration: BoxDecoration(
|
||||
color: cs.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.serverTrustMincifryTitle,
|
||||
style: TextStyle(
|
||||
color: cs.onSurface,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
l10n.serverTrustMincifrySubtitle,
|
||||
style: TextStyle(
|
||||
color: cs.onSurfaceVariant,
|
||||
fontSize: 12.5,
|
||||
height: 1.3,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Switch(
|
||||
value: _trustMincifryCa,
|
||||
onChanged: _busy
|
||||
? null
|
||||
: (v) => setState(() => _trustMincifryCa = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton(
|
||||
onPressed: _busy ? null : () => _apply(l10n),
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"serverSettingsTitle": "Server",
|
||||
"serverHostLabel": "Host",
|
||||
"serverPortLabel": "Port",
|
||||
"serverTrustMincifryTitle": "Trust the Минцифры CA",
|
||||
"serverTrustMincifrySubtitle": "Required for api2.oneme.ru: its certificate chains to the Russian Trusted Root CA, which is absent from the standard trust store. The root is bundled with the app; other hosts keep using the usual roots.",
|
||||
"serverApply": "Apply and reconnect",
|
||||
"serverUseDefault": "Reset to default",
|
||||
"serverInvalidHostOrPort": "Enter a valid host and port (1–65535)",
|
||||
|
||||
@@ -206,6 +206,18 @@ abstract class AppLocalizations {
|
||||
/// **'Port'**
|
||||
String get serverPortLabel;
|
||||
|
||||
/// No description provided for @serverTrustMincifryTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Trust the Минцифры CA'**
|
||||
String get serverTrustMincifryTitle;
|
||||
|
||||
/// No description provided for @serverTrustMincifrySubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Required for api2.oneme.ru: its certificate chains to the Russian Trusted Root CA, which is absent from the standard trust store. The root is bundled with the app; other hosts keep using the usual roots.'**
|
||||
String get serverTrustMincifrySubtitle;
|
||||
|
||||
/// No description provided for @serverApply.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -63,6 +63,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get serverPortLabel => 'Port';
|
||||
|
||||
@override
|
||||
String get serverTrustMincifryTitle => 'Trust the Минцифры CA';
|
||||
|
||||
@override
|
||||
String get serverTrustMincifrySubtitle =>
|
||||
'Required for api2.oneme.ru: its certificate chains to the Russian Trusted Root CA, which is absent from the standard trust store. The root is bundled with the app; other hosts keep using the usual roots.';
|
||||
|
||||
@override
|
||||
String get serverApply => 'Apply and reconnect';
|
||||
|
||||
|
||||
@@ -64,6 +64,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get serverPortLabel => 'Порт';
|
||||
|
||||
@override
|
||||
String get serverTrustMincifryTitle => 'Доверять сертификату Минцифры';
|
||||
|
||||
@override
|
||||
String get serverTrustMincifrySubtitle =>
|
||||
'Нужно для api2.oneme.ru: его сертификат выпущен под корнем Russian Trusted Root CA, которого нет в обычном хранилище. Корень зашит в приложение, остальные хосты проверяются как раньше.';
|
||||
|
||||
@override
|
||||
String get serverApply => 'Применить и переподключиться';
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"serverSettingsTitle": "Сервер",
|
||||
"serverHostLabel": "Хост",
|
||||
"serverPortLabel": "Порт",
|
||||
"serverTrustMincifryTitle": "Доверять сертификату Минцифры",
|
||||
"serverTrustMincifrySubtitle": "Нужно для api2.oneme.ru: его сертификат выпущен под корнем Russian Trusted Root CA, которого нет в обычном хранилище. Корень зашит в приложение, остальные хосты проверяются как раньше.",
|
||||
"serverApply": "Применить и переподключиться",
|
||||
"serverUseDefault": "Сбросить к умолчанию",
|
||||
"serverInvalidHostOrPort": "Укажите корректный хост и порт (1–65535)",
|
||||
|
||||
@@ -174,6 +174,7 @@ void _installLogCapture() {
|
||||
void main(List<String> args) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await initKolibri();
|
||||
await TlsConfig.applyMincifryTrust();
|
||||
DebugTest.parse(args);
|
||||
_installLogCapture();
|
||||
VideoPlayerMediaKit.ensureInitialized(
|
||||
|
||||
@@ -86,6 +86,8 @@ PODS:
|
||||
- GoogleUtilities/Privacy
|
||||
- kolibri (0.0.1):
|
||||
- FlutterMacOS
|
||||
- komet_crypto (0.0.1):
|
||||
- FlutterMacOS
|
||||
- media_kit_libs_macos_video (1.0.4):
|
||||
- FlutterMacOS
|
||||
- media_kit_video (0.0.1):
|
||||
@@ -143,6 +145,7 @@ DEPENDENCIES:
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin`)
|
||||
- kolibri (from `Flutter/ephemeral/.symlinks/plugins/kolibri/macos`)
|
||||
- komet_crypto (from `Flutter/ephemeral/.symlinks/plugins/komet_crypto/macos`)
|
||||
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
|
||||
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
|
||||
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`)
|
||||
@@ -201,6 +204,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/darwin
|
||||
kolibri:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/kolibri/macos
|
||||
komet_crypto:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/komet_crypto/macos
|
||||
media_kit_libs_macos_video:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
|
||||
media_kit_video:
|
||||
@@ -252,6 +257,7 @@ SPEC CHECKSUMS:
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 4f2618a4a1e762a1ee134a1e2323bba9843e06da
|
||||
kolibri: 93062ece67f68ec0b909876b527aa15e198b4a73
|
||||
komet_crypto: 856fa27dc180350f88a7cf6de6b512d2d221b737
|
||||
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
|
||||
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
|
||||
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
|
||||
|
||||
+7
-6
@@ -601,10 +601,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
|
||||
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
version: "3.1.0"
|
||||
geolocator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -760,10 +760,11 @@ packages:
|
||||
kolibri:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "third_party/kolibri/kolibri-dart"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.0"
|
||||
name: kolibri
|
||||
sha256: e8ed4eab5687204a77d449743ceb73b0797f5702afb0a7841610a127fc102710
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
komet_crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
+3
-3
@@ -35,9 +35,9 @@ dependencies:
|
||||
intl: any
|
||||
|
||||
# Rust networking core (kolibri) — FFI plugin, replaces the Dart transport.
|
||||
# Pinned to the third_party/kolibri submodule.
|
||||
kolibri:
|
||||
path: third_party/kolibri/kolibri-dart
|
||||
# Published from the KometTeam/kolibri repo; the native core is compiled at
|
||||
# app build time and pulled from that repo by git tag.
|
||||
kolibri: ^0.1.2
|
||||
|
||||
# Rust message-encryption core — Argon2id + ChaCha20-Poly1305, output encoded
|
||||
# as lowercase Cyrillic base32. Separate from kolibri: that is vendored
|
||||
|
||||
Vendored
-1
Submodule third_party/kolibri deleted from 1aa610f55b
Reference in New Issue
Block a user