287 lines
7.5 KiB
Dart
287 lines
7.5 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:file_picker/file_picker.dart';
|
||
import 'package:process_run/shell.dart';
|
||
import 'dart:io';
|
||
import 'dart:convert';
|
||
import 'package:path_provider/path_provider.dart';
|
||
import 'package:path/path.dart' as path;
|
||
|
||
void main() {
|
||
runApp(NaiveProxyApp());
|
||
}
|
||
|
||
class ServerConfig {
|
||
final String name;
|
||
final String configPath;
|
||
final int? ping;
|
||
|
||
ServerConfig({
|
||
required this.name,
|
||
required this.configPath,
|
||
this.ping
|
||
});
|
||
}
|
||
|
||
class NaiveProxyApp extends StatelessWidget {
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return MaterialApp(
|
||
title: 'NaiveProxy Launcher',
|
||
theme: ThemeData(
|
||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
|
||
useMaterial3: true,
|
||
),
|
||
home: NaiveProxyHomePage(),
|
||
);
|
||
}
|
||
}
|
||
|
||
class NaiveProxyHomePage extends StatefulWidget {
|
||
@override
|
||
_NaiveProxyHomePageState createState() => _NaiveProxyHomePageState();
|
||
}
|
||
|
||
class _NaiveProxyHomePageState extends State<NaiveProxyHomePage> {
|
||
List<ServerConfig> _servers = [];
|
||
ServerConfig? _selectedServer;
|
||
bool _isProxyRunning = false;
|
||
Process? _naiveProcess;
|
||
List<String> _logs = [];
|
||
ScrollController _logController = ScrollController();
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_loadServers();
|
||
}
|
||
|
||
Future<void> _loadServers() async {
|
||
// 获取配置目录
|
||
Directory configDir = await _getNaiveFlyConfigDirectory();
|
||
|
||
// 确保目录存在
|
||
if (!configDir.existsSync()) {
|
||
configDir.createSync(recursive: true);
|
||
}
|
||
|
||
// 读取所有JSON文件
|
||
List<FileSystemEntity> files = configDir.listSync()
|
||
.where((file) => file.path.endsWith('.json'))
|
||
.toList();
|
||
|
||
setState(() {
|
||
_servers = files.map((file) {
|
||
String name = path.basenameWithoutExtension(file.path);
|
||
return ServerConfig(
|
||
name: name,
|
||
configPath: file.path
|
||
);
|
||
}).toList();
|
||
});
|
||
}
|
||
|
||
Future<Directory> _getNaiveFlyConfigDirectory() async {
|
||
if (Platform.isWindows) {
|
||
final appDataDir = Platform.environment['APPDATA'];
|
||
return Directory(path.join(appDataDir!, 'NaiveFly'));
|
||
} else {
|
||
// 假设是Unix类系统(Linux/macOS)
|
||
final homeDir = Platform.environment['HOME'];
|
||
return Directory(path.join(homeDir!, '.config', 'naivefly'));
|
||
}
|
||
}
|
||
|
||
Future<void> _launchNaiveProxy() async {
|
||
if (_isProxyRunning) {
|
||
_stopNaiveProxy();
|
||
return;
|
||
}
|
||
|
||
if (_selectedServer == null) {
|
||
_showErrorDialog('Please select a server');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 检查naiveproxy是否已安装
|
||
var whichResult = await Shell().run('which naive');
|
||
|
||
if (whichResult.isEmpty) {
|
||
_showErrorDialog('NaiveProxy is not installed');
|
||
return;
|
||
}
|
||
|
||
// 启动naive进程
|
||
_naiveProcess = await Process.start('naive', [_selectedServer!.configPath]);
|
||
|
||
setState(() {
|
||
_isProxyRunning = true;
|
||
_logs.clear();
|
||
_addLog('NaiveProxy started: ${_selectedServer!.name}');
|
||
});
|
||
|
||
// 监听进程输出
|
||
_naiveProcess!.stdout.listen((List<int> event) {
|
||
setState(() {
|
||
_addLog('Output: ${String.fromCharCodes(event)}');
|
||
});
|
||
});
|
||
|
||
// 监听进程错误输出
|
||
_naiveProcess!.stderr.listen((List<int> event) {
|
||
setState(() {
|
||
_addLog('Message: ${String.fromCharCodes(event)}');
|
||
});
|
||
});
|
||
|
||
// 监听进程退出
|
||
_naiveProcess!.exitCode.then((int code) {
|
||
setState(() {
|
||
_isProxyRunning = false;
|
||
_addLog('NaiveProxy exited with code: $code');
|
||
});
|
||
});
|
||
|
||
} catch (e) {
|
||
_showErrorDialog('Launch failed: ${e.toString()}');
|
||
}
|
||
}
|
||
|
||
void _stopNaiveProxy() {
|
||
if (_naiveProcess != null) {
|
||
_naiveProcess!.kill();
|
||
setState(() {
|
||
_isProxyRunning = false;
|
||
_addLog('NaiveProxy manually stopped');
|
||
});
|
||
}
|
||
}
|
||
|
||
void _addLog(String message) {
|
||
setState(() {
|
||
_logs.add(message);
|
||
// 自动滚动到底部
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
if (_logController.hasClients) {
|
||
_logController.animateTo(
|
||
_logController.position.maxScrollExtent,
|
||
duration: Duration(milliseconds: 300),
|
||
curve: Curves.easeOut,
|
||
);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
void _showErrorDialog(String message) {
|
||
showDialog(
|
||
context: context,
|
||
builder: (BuildContext context) {
|
||
return AlertDialog(
|
||
title: Text('Error'),
|
||
content: Text(message),
|
||
actions: <Widget>[
|
||
TextButton(
|
||
child: Text('OK'),
|
||
onPressed: () {
|
||
Navigator.of(context).pop();
|
||
},
|
||
),
|
||
],
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: Text('NaiveProxy Launcher'),
|
||
centerTitle: true,
|
||
actions: [
|
||
IconButton(
|
||
icon: Icon(Icons.refresh),
|
||
onPressed: _loadServers,
|
||
)
|
||
],
|
||
),
|
||
body: Column(
|
||
children: <Widget>[
|
||
// 服务器列表
|
||
Container(
|
||
height: 200,
|
||
child: ListView.builder(
|
||
itemCount: _servers.length,
|
||
itemBuilder: (context, index) {
|
||
ServerConfig server = _servers[index];
|
||
return ListTile(
|
||
title: Text(server.name),
|
||
subtitle: Text(server.configPath),
|
||
trailing: Text('${server.ping ?? 'N/A'} ms',
|
||
style: TextStyle(color: Colors.green),
|
||
),
|
||
selected: _selectedServer == server,
|
||
onTap: () {
|
||
setState(() {
|
||
_selectedServer = server;
|
||
});
|
||
},
|
||
);
|
||
},
|
||
),
|
||
),
|
||
|
||
// 启动按钮
|
||
Padding(
|
||
padding: const EdgeInsets.all(16.0),
|
||
child: ElevatedButton.icon(
|
||
icon: Icon(_isProxyRunning ? Icons.stop : Icons.rocket_launch),
|
||
label: Text(_isProxyRunning ? 'Stop Proxy' : 'Start NaiveProxy'),
|
||
onPressed: _launchNaiveProxy,
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: _isProxyRunning ? Colors.red : Colors.green,
|
||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||
),
|
||
),
|
||
),
|
||
|
||
// 日志输出区域
|
||
Expanded(
|
||
child: Container(
|
||
margin: EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
border: Border.all(color: Colors.grey),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: ListView.builder(
|
||
controller: _logController,
|
||
itemCount: _logs.length,
|
||
itemBuilder: (context, index) {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||
child: Text(
|
||
_logs[index],
|
||
style: TextStyle(
|
||
fontFamily: 'monospace',
|
||
fontSize: 12,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_stopNaiveProxy();
|
||
_logController.dispose();
|
||
super.dispose();
|
||
}
|
||
}
|