add some basic funcition

This commit is contained in:
2025-03-28 15:03:46 +08:00
parent eaf8bbabb5
commit bb2c25babc
3 changed files with 274 additions and 84 deletions

View File

@ -2,21 +2,34 @@ 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 NaiveProxyApp extends StatelessWidget {
const NaiveProxyApp({super.key});
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启动器',
title: 'NaiveProxy Launcher',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple),
useMaterial3: true,
),
home: NaiveProxyHomePage(),
);
@ -24,89 +37,152 @@ class NaiveProxyApp extends StatelessWidget {
}
class NaiveProxyHomePage extends StatefulWidget {
const NaiveProxyHomePage({super.key});
@override
_NaiveProxyHomePageState createState() => _NaiveProxyHomePageState();
}
class _NaiveProxyHomePageState extends State<NaiveProxyHomePage> {
String? _configFilePath;
String _status = '未选择配置文件';
List<ServerConfig> _servers = [];
ServerConfig? _selectedServer;
bool _isProxyRunning = false;
Process? _naiveProcess;
List<String> _logs = [];
ScrollController _logController = ScrollController();
Future<void> _pickConfigFile() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
);
@override
void initState() {
super.initState();
_loadServers();
}
if (result != null) {
setState(() {
_configFilePath = result.files.single.path;
_status = '已选择文件: ${_configFilePath!.split('/').last}';
});
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 (_configFilePath == null) {
_showErrorDialog('请先导入配置文件');
if (_isProxyRunning) {
_stopNaiveProxy();
return;
}
if (_selectedServer == null) {
_showErrorDialog('Please select a server');
return;
}
try {
// 检查naiveproxy是否已安装
var shell = Shell();
var whichResult = await shell.run('which naive');
var whichResult = await Shell().run('which naive');
if (whichResult.isEmpty) {
_showErrorDialog('NaiveProxy未安装');
_showErrorDialog('NaiveProxy is not installed');
return;
}
// 尝试执行naive
var result = await shell.run('naive $_configFilePath');
// 启动naive进程
_naiveProcess = await Process.start('naive', [_selectedServer!.configPath]);
// 显示执行结果
_showResultDialog('NaiveProxy已启动', result.outText);
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('启动失败:${e.toString()}');
_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('错误'),
title: Text('Error'),
content: Text(message),
actions: <Widget>[
TextButton(
child: Text('确定'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
void _showResultDialog(String title, String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: SingleChildScrollView(
child: Text(message),
),
actions: <Widget>[
TextButton(
child: Text('确定'),
child: Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
@ -121,43 +197,90 @@ class _NaiveProxyHomePageState extends State<NaiveProxyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('NaiveProxy启动器'),
title: Text('NaiveProxy Launcher'),
centerTitle: true,
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _loadServers,
)
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 300,
child: Text(
_status,
style: TextStyle(fontSize: 16),
textAlign: TextAlign.center,
),
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;
});
},
);
},
),
SizedBox(height: 20),
ElevatedButton.icon(
icon: Icon(Icons.file_upload),
label: Text('导入配置文件'),
onPressed: _pickConfigFile,
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
),
SizedBox(height: 20),
ElevatedButton.icon(
icon: Icon(Icons.rocket_launch),
label: Text('启动NaiveProxy'),
),
// 启动按钮
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: Colors.green,
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();
}
}