add: cloudflared/Cloudflare Tunnel support

Fixes #949
This commit is contained in:
lollipopkit🏳️‍⚧️
2025-11-22 17:16:52 +08:00
parent 84921de7a7
commit c4c0fdf6ff
6 changed files with 23 additions and 15 deletions

View File

@@ -29,6 +29,7 @@ class ExecutableInfo {
/// Generic executable manager for downloading and managing external tools
abstract final class ExecutableManager {
static const String _executablesDirName = 'executables';
static const int _posixExecuteBitsMask = 0x49; // Equivalent to POSIX octal 0o111
static late final Directory _executablesDir;
static final Map<String, ExecutableInfo> _customExecutables = {};
static bool _customExecutablesLoaded = false;
@@ -137,7 +138,7 @@ abstract final class ExecutableManager {
return await getExecutablePath(name);
}
return await getExecutablePath(name);
throw ExecutableException('Executable "$name" not found and automatic installation is not implemented');
}
/// Remove a local executable
@@ -270,7 +271,7 @@ abstract final class ExecutableManager {
} else {
// Check file permissions
final stat = file.statSync();
return (stat.mode & 0x111) != 0; // Check execute bits
return (stat.mode & _posixExecuteBitsMask) != 0; // Check execute bits
}
}

View File

@@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:server_box/core/utils/proxy_command_executor.dart' show ProxyCommandException;
part 'proxy_command_config.freezed.dart';
part 'proxy_command_config.g.dart';
@@ -65,6 +66,12 @@ const Map<String, ProxyCommandConfig> proxyCommandPresets = {
extension ProxyCommandConfigExtension on ProxyCommandConfig {
/// Get the final command with placeholders replaced
String getFinalCommand({required String hostname, required int port, required String user}) {
if (!command.contains('%h') && !command.contains('%p') && !command.contains('%r')) {
throw ProxyCommandException(
message: 'Proxy command "$command" must include at least one placeholder (%h, %p, %r)',
);
}
var finalCommand = command;
finalCommand = finalCommand.replaceAll('%h', hostname);
finalCommand = finalCommand.replaceAll('%p', port.toString());

View File

@@ -22,7 +22,7 @@ part 'server_private_info.g.dart';
abstract class Spi with _$Spi {
const Spi._();
@JsonSerializable(includeIfNull: false)
@JsonSerializable(includeIfNull: false, explicitToJson: true)
const factory Spi({
required String name,
required String ip,

View File

@@ -237,7 +237,7 @@ return $default(_that.name,_that.ip,_that.port,_that.user,_that.pwd,_that.keyId,
/// @nodoc
@JsonSerializable(includeIfNull: false)
@JsonSerializable(includeIfNull: false, explicitToJson: true)
class _Spi extends Spi {
const _Spi({required this.name, required this.ip, required this.port, required this.user, this.pwd, @JsonKey(name: 'pubKeyId') this.keyId, final List<String>? tags, this.alterUrl, this.autoConnect = true, this.jumpId, this.custom, this.wolCfg, final Map<String, String>? envs, @JsonKey(fromJson: Spi.parseId) this.id = '', this.customSystemType, final List<String>? disabledCmdTypes, this.proxyCommand}): _tags = tags,_envs = envs,_disabledCmdTypes = disabledCmdTypes,super._();
factory _Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);

View File

@@ -52,13 +52,13 @@ Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
'alterUrl': ?instance.alterUrl,
'autoConnect': instance.autoConnect,
'jumpId': ?instance.jumpId,
'custom': ?instance.custom,
'wolCfg': ?instance.wolCfg,
'custom': ?instance.custom?.toJson(),
'wolCfg': ?instance.wolCfg?.toJson(),
'envs': ?instance.envs,
'id': instance.id,
'customSystemType': ?_$SystemTypeEnumMap[instance.customSystemType],
'disabledCmdTypes': ?instance.disabledCmdTypes,
'proxyCommand': ?instance.proxyCommand,
'proxyCommand': ?instance.proxyCommand?.toJson(),
};
const _$SystemTypeEnumMap = {

View File

@@ -1007,10 +1007,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
mime:
dependency: transitive
description:
@@ -1581,26 +1581,26 @@ packages:
dependency: "direct dev"
description:
name: test
sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb"
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
url: "https://pub.dev"
source: hosted
version: "1.26.2"
version: "1.26.3"
test_api:
dependency: transitive
description:
name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.6"
version: "0.7.7"
test_core:
dependency: transitive
description:
name: test_core
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a"
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
url: "https://pub.dev"
source: hosted
version: "0.6.11"
version: "0.6.12"
timing:
dependency: transitive
description: