Files
naivefly/lib/main.dart
2025-03-28 15:03:46 +08:00

287 lines
7.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}