Add proxy routing and sing-box support

This commit is contained in:
Fabian Freund
2026-05-22 18:16:31 +02:00
parent 51289f1266
commit a5974617aa
262 changed files with 32003 additions and 3962 deletions
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart';
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_profiles.dart';
import 'package:weblibre/features/user/data/database/definitions.drift.dart'
show ProxyProfile;
import 'package:weblibre/features/user/data/models/proxy_routing_settings.dart';
import 'package:weblibre/features/user/domain/repositories/proxy_routing_settings.dart';
part 'assigned_proxy_profiles.g.dart';
/// Returns sing-box proxy profiles that have at least one routing assignment:
/// global regular-tab routing, private-tab routing, or any container.
@Riverpod(keepAlive: true)
List<ProxyProfile> assignedSingboxProxyProfiles(Ref ref) {
final profiles =
ref.watch(singboxProxyProfilesRepositoryProvider).value ?? const [];
if (profiles.isEmpty) return const [];
final routing = ref.watch(proxyRoutingSettingsWithDefaultsProvider);
final containers =
ref.watch(watchContainersWithCountProvider).value ?? const [];
final assignedConnectionIds = <String>{
if (routing.regularTabsMode == ProxyRegularTabRoutingMode.all &&
routing.regularTabsProxyConnectionId != null)
routing.regularTabsProxyConnectionId!.encode(),
if (routing.privateTabsProxyConnectionId != null)
routing.privateTabsProxyConnectionId!.encode(),
for (final container in containers)
if (container.metadata.proxyConnectionId != null)
container.metadata.proxyConnectionId!.encode(),
};
if (assignedConnectionIds.isEmpty) return const [];
return [
for (final profile in profiles)
if (assignedConnectionIds.contains(profile.proxyConnectionId)) profile,
];
}
@@ -0,0 +1,66 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'assigned_proxy_profiles.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Returns sing-box proxy profiles that have at least one routing assignment:
/// global regular-tab routing, private-tab routing, or any container.
@ProviderFor(assignedSingboxProxyProfiles)
final assignedSingboxProxyProfilesProvider =
AssignedSingboxProxyProfilesProvider._();
/// Returns sing-box proxy profiles that have at least one routing assignment:
/// global regular-tab routing, private-tab routing, or any container.
final class AssignedSingboxProxyProfilesProvider
extends
$FunctionalProvider<
List<ProxyProfile>,
List<ProxyProfile>,
List<ProxyProfile>
>
with $Provider<List<ProxyProfile>> {
/// Returns sing-box proxy profiles that have at least one routing assignment:
/// global regular-tab routing, private-tab routing, or any container.
AssignedSingboxProxyProfilesProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'assignedSingboxProxyProfilesProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$assignedSingboxProxyProfilesHash();
@$internal
@override
$ProviderElement<List<ProxyProfile>> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
List<ProxyProfile> create(Ref ref) {
return assignedSingboxProxyProfiles(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(List<ProxyProfile> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<List<ProxyProfile>>(value),
);
}
}
String _$assignedSingboxProxyProfilesHash() =>
r'dbc5c429a3b5e4bff902023d363e568c2f955fc4';
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
* This file is part of WebLibre
* (see https://weblibre.eu).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:fast_equatable/fast_equatable.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart';
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
import 'package:weblibre/features/proxy/domain/extensions/singbox_proxy_profile_type_x.dart';
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_profiles.dart';
part 'proxy_connection_options.g.dart';
class ProxyConnectionOption with FastEquatable {
final ProxyConnectionId id;
final String title;
final String subtitle;
ProxyConnectionOption({
required this.id,
required this.title,
required this.subtitle,
});
@override
List<Object?> get hashParameters => [id, title, subtitle];
}
@Riverpod(keepAlive: true)
List<ProxyConnectionOption> proxyConnectionOptions(Ref ref) {
final profilesAsync = ref.watch(singboxProxyProfilesRepositoryProvider);
profilesAsync.whenOrNull(
error: (error, stackTrace) => logger.e(
'Failed to load sing-box proxy profiles for connection picker',
error: error,
stackTrace: stackTrace,
),
);
final singboxProfiles = profilesAsync.value ?? const [];
return [
ProxyConnectionOption(
id: const TorProxyConnectionId(),
title: 'Tor',
subtitle: 'Route through the Tor network',
),
for (final profile in singboxProfiles)
ProxyConnectionOption(
id: profile.proxyConnection,
title: profile.name,
subtitle: profile.type.label,
),
];
}
String proxyConnectionTitle(
List<ProxyConnectionOption> options,
ProxyConnectionId proxyConnectionId,
) {
for (final option in options) {
if (option.id == proxyConnectionId) {
return option.title;
}
}
return 'Unknown proxy';
}
@@ -0,0 +1,58 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'proxy_connection_options.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(proxyConnectionOptions)
final proxyConnectionOptionsProvider = ProxyConnectionOptionsProvider._();
final class ProxyConnectionOptionsProvider
extends
$FunctionalProvider<
List<ProxyConnectionOption>,
List<ProxyConnectionOption>,
List<ProxyConnectionOption>
>
with $Provider<List<ProxyConnectionOption>> {
ProxyConnectionOptionsProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'proxyConnectionOptionsProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$proxyConnectionOptionsHash();
@$internal
@override
$ProviderElement<List<ProxyConnectionOption>> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
List<ProxyConnectionOption> create(Ref ref) {
return proxyConnectionOptions(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(List<ProxyConnectionOption> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<List<ProxyConnectionOption>>(value),
);
}
}
String _$proxyConnectionOptionsHash() =>
r'7832968f74acd98189110cf926208e694a20d55a';