feat: api2.oneme.ru с корнем Минцифры; kolibri с pub.dev вместо сабмодуля
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user