feat: Wake On LAN

This commit is contained in:
lollipopkit
2024-05-09 23:27:05 +08:00
parent b876981243
commit e2ddd48a79
23 changed files with 290 additions and 94 deletions

View File

@@ -690,7 +690,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -700,7 +700,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -826,7 +826,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -836,7 +836,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -854,7 +854,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -864,7 +864,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -885,7 +885,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -898,7 +898,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -924,7 +924,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -937,7 +937,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -960,7 +960,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -973,7 +973,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -996,7 +996,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1008,7 +1008,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1037,7 +1037,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1049,7 +1049,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;
@@ -1075,7 +1075,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1087,7 +1087,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;

View File

@@ -1,6 +1,7 @@
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:toolbox/data/model/server/custom.dart'; import 'package:toolbox/data/model/server/custom.dart';
import 'package:toolbox/data/model/server/server.dart'; import 'package:toolbox/data/model/server/server.dart';
import 'package:toolbox/data/model/server/wol_cfg.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
import '../app/error.dart'; import '../app/error.dart';
@@ -38,6 +39,9 @@ class ServerPrivateInfo {
@HiveField(10) @HiveField(10)
final ServerCustom? custom; final ServerCustom? custom;
@HiveField(11)
final WakeOnLanCfg? wolCfg;
final String id; final String id;
const ServerPrivateInfo({ const ServerPrivateInfo({
@@ -52,6 +56,7 @@ class ServerPrivateInfo {
this.autoConnect, this.autoConnect,
this.jumpId, this.jumpId,
this.custom, this.custom,
this.wolCfg,
}) : id = '$user@$ip:$port'; }) : id = '$user@$ip:$port';
static ServerPrivateInfo fromJson(Map<String, dynamic> json) { static ServerPrivateInfo fromJson(Map<String, dynamic> json) {
@@ -68,6 +73,9 @@ class ServerPrivateInfo {
final custom = json["customCmd"] == null final custom = json["customCmd"] == null
? null ? null
: ServerCustom.fromJson(json["custom"].cast<String, dynamic>()); : ServerCustom.fromJson(json["custom"].cast<String, dynamic>());
final wolCfg = json["wolCfg"] == null
? null
: WakeOnLanCfg.fromJson(json["wolCfg"].cast<String, dynamic>());
return ServerPrivateInfo( return ServerPrivateInfo(
name: name, name: name,
@@ -81,6 +89,7 @@ class ServerPrivateInfo {
autoConnect: autoConnect, autoConnect: autoConnect,
jumpId: jumpId, jumpId: jumpId,
custom: custom, custom: custom,
wolCfg: wolCfg,
); );
} }
@@ -111,6 +120,9 @@ class ServerPrivateInfo {
if (custom != null) { if (custom != null) {
data["custom"] = custom?.toJson(); data["custom"] = custom?.toJson();
} }
if (wolCfg != null) {
data["wolCfg"] = wolCfg?.toJson();
}
return data; return data;
} }

View File

@@ -28,13 +28,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
autoConnect: fields[8] as bool?, autoConnect: fields[8] as bool?,
jumpId: fields[9] as String?, jumpId: fields[9] as String?,
custom: fields[10] as ServerCustom?, custom: fields[10] as ServerCustom?,
wolCfg: fields[11] as WakeOnLanCfg?,
); );
} }
@override @override
void write(BinaryWriter writer, ServerPrivateInfo obj) { void write(BinaryWriter writer, ServerPrivateInfo obj) {
writer writer
..writeByte(11) ..writeByte(12)
..writeByte(0) ..writeByte(0)
..write(obj.name) ..write(obj.name)
..writeByte(1) ..writeByte(1)
@@ -56,7 +57,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
..writeByte(9) ..writeByte(9)
..write(obj.jumpId) ..write(obj.jumpId)
..writeByte(10) ..writeByte(10)
..write(obj.custom); ..write(obj.custom)
..writeByte(11)
..write(obj.wolCfg);
} }
@override @override

View File

@@ -0,0 +1,71 @@
import 'dart:io';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:wake_on_lan/wake_on_lan.dart';
@HiveType(typeId: 8)
final class WakeOnLanCfg {
@HiveField(0)
final String mac;
@HiveField(1)
final String ip;
@HiveField(2)
final String? pwd;
const WakeOnLanCfg({
required this.mac,
required this.ip,
this.pwd,
});
(Object?, bool) validate() {
final macValidation = MACAddress.validate(mac);
final ipValidation = IPAddress.validate(
ip,
type: ip.contains(':')
? InternetAddressType.IPv6
: InternetAddressType.IPv4,
);
final pwdValidation = SecureONPassword.validate(pwd);
final valid =
macValidation.state && ipValidation.state && pwdValidation.state;
final err =
macValidation.error ?? ipValidation.error ?? pwdValidation.error;
return (err, valid);
}
Future<void> wake() async {
if (!validate().$2) {
throw Exception('Invalid WakeOnLanCfg');
}
final ip_ = IPAddress(ip);
final mac_ = MACAddress(mac);
final pwd_ = pwd != null ? SecureONPassword(pwd!) : null;
final obj = WakeOnLAN(ip_, mac_, password: pwd_);
await obj.wake(
repeat: 3,
repeatDelay: const Duration(milliseconds: 500),
);
}
static WakeOnLanCfg fromJson(Map<String, dynamic> json) {
return WakeOnLanCfg(
mac: json['mac'] as String,
ip: json['ip'] as String,
pwd: json['pwd'] as String?,
);
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{
'mac': mac,
'ip': ip,
};
if (pwd != null) {
map['pwd'] = pwd;
}
return map;
}
}

View File

@@ -141,8 +141,7 @@ class ServerProvider extends ChangeNotifier {
TryLimiter.reset(s.spi.id); TryLimiter.reset(s.spi.id);
} }
if (!(s.spi.autoConnect ?? true) && if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected ||
s.conn == ServerConn.disconnected ||
_manualDisconnectedIds.contains(s.spi.id)) { _manualDisconnectedIds.contains(s.spi.id)) {
return; return;
} }
@@ -277,6 +276,23 @@ class ServerProvider extends ChangeNotifier {
if (s.needGenClient || (s.client?.isClosed ?? true)) { if (s.needGenClient || (s.client?.isClosed ?? true)) {
_setServerState(s, ServerConn.connecting); _setServerState(s, ServerConn.connecting);
final wol = spi.wolCfg;
if (wol != null) {
/// TODO: test it
try {
await wol.wake();
} catch (e) {
// TryLimiter.inc(sid);
// s.status.err = SSHErr(
// type: SSHErrType.connect,
// message: 'Wake on lan failed: $e',
// );
// _setServerState(s, ServerConn.failed);
// Loggers.app.warning('Wake on lan failed', e);
// return;
}
}
try { try {
final time1 = DateTime.now(); final time1 = DateTime.now();
s.client = await genClient( s.client = await genClient(

View File

@@ -2,9 +2,9 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 884; static const int build = 887;
static const String engine = "3.19.6"; static const String engine = "3.19.6";
static const String buildAt = "2024-05-09 17:08:49"; static const String buildAt = "2024-05-09 23:08:15";
static const int modifications = 4; static const int modifications = 22;
static const int script = 46; static const int script = 46;
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav-Einstellungen sind leer", "webdavSettingEmpty": "Webdav-Einstellungen sind leer",
"whenOpenApp": "Beim Öffnen der App", "whenOpenApp": "Beim Öffnen der App",
"willTakEeffectImmediately": "Wird sofort angewendet", "willTakEeffectImmediately": "Wird sofort angewendet",
"wolTip": "Nach der Konfiguration von WOL (Wake-on-LAN) wird jedes Mal, wenn der Server verbunden wird, eine WOL-Anfrage gesendet.",
"write": "Schreiben", "write": "Schreiben",
"writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht." "writeScriptFailTip": "Das Schreiben des Skripts ist fehlgeschlagen, möglicherweise aufgrund fehlender Berechtigungen oder das Verzeichnis existiert nicht."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav setting is empty", "webdavSettingEmpty": "Webdav setting is empty",
"whenOpenApp": "When opening the app", "whenOpenApp": "When opening the app",
"willTakEeffectImmediately": "Will take effect immediately", "willTakEeffectImmediately": "Will take effect immediately",
"wolTip": "After configuring WOL (Wake-on-LAN), a WOL request is sent each time the server is connected.",
"write": "Write", "write": "Write",
"writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist." "writeScriptFailTip": "Writing to the script failed, possibly due to lack of permissions or the directory does not exist."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "La configuración de Webdav está vacía", "webdavSettingEmpty": "La configuración de Webdav está vacía",
"whenOpenApp": "Al abrir la App", "whenOpenApp": "Al abrir la App",
"willTakEeffectImmediately": "Los cambios tendrán efecto inmediatamente", "willTakEeffectImmediately": "Los cambios tendrán efecto inmediatamente",
"wolTip": "Después de configurar WOL (Wake-on-LAN), se envía una solicitud de WOL cada vez que se conecta el servidor.",
"write": "Escribir", "write": "Escribir",
"writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe." "writeScriptFailTip": "La escritura en el script falló, posiblemente por falta de permisos o porque el directorio no existe."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "La configuration Webdav est vide", "webdavSettingEmpty": "La configuration Webdav est vide",
"whenOpenApp": "À l'ouverture de l'application", "whenOpenApp": "À l'ouverture de l'application",
"willTakEeffectImmediately": "Prendra effet immédiatement", "willTakEeffectImmediately": "Prendra effet immédiatement",
"wolTip": "Après avoir configuré WOL (Wake-on-LAN), une requête WOL est envoyée à chaque connexion au serveur.",
"write": "Écrire", "write": "Écrire",
"writeScriptFailTip": "L'écriture du script a échoué, peut-être en raison d'un manque de permissions ou parce que le répertoire n'existe pas." "writeScriptFailTip": "L'écriture du script a échoué, peut-être en raison d'un manque de permissions ou parce que le répertoire n'existe pas."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Pengaturan webdav kosong", "webdavSettingEmpty": "Pengaturan webdav kosong",
"whenOpenApp": "Saat membuka aplikasi", "whenOpenApp": "Saat membuka aplikasi",
"willTakEeffectImmediately": "Akan segera berlaku", "willTakEeffectImmediately": "Akan segera berlaku",
"wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.",
"write": "Tulis", "write": "Tulis",
"writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada." "writeScriptFailTip": "Penulisan ke skrip gagal, mungkin karena tidak ada izin atau direktori tidak ada."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdavの設定が空です", "webdavSettingEmpty": "Webdavの設定が空です",
"whenOpenApp": "アプリを開くとき", "whenOpenApp": "アプリを開くとき",
"willTakEeffectImmediately": "変更は即座に有効になります", "willTakEeffectImmediately": "変更は即座に有効になります",
"wolTip": "WOLWake-on-LANを設定した後、サーバーに接続するたびにWOLリクエストが送信されます。",
"write": "書き込み", "write": "書き込み",
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。" "writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。"
} }

View File

@@ -327,6 +327,7 @@
"webdavSettingEmpty": "Webdav-instelling is leeg", "webdavSettingEmpty": "Webdav-instelling is leeg",
"whenOpenApp": "Bij het openen van de app", "whenOpenApp": "Bij het openen van de app",
"willTakEeffectImmediately": "Zal onmiddellijk van kracht worden", "willTakEeffectImmediately": "Zal onmiddellijk van kracht worden",
"wolTip": "Na het configureren van WOL (Wake-on-LAN), wordt elke keer dat de server wordt verbonden een WOL-verzoek verzonden.",
"write": "Schrijven", "write": "Schrijven",
"writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat." "writeScriptFailTip": "Het schrijven naar het script is mislukt, mogelijk door gebrek aan rechten of omdat de map niet bestaat."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Configurações de Webdav estão vazias", "webdavSettingEmpty": "Configurações de Webdav estão vazias",
"whenOpenApp": "Ao abrir o app", "whenOpenApp": "Ao abrir o app",
"willTakEeffectImmediately": "As alterações serão aplicadas imediatamente", "willTakEeffectImmediately": "As alterações serão aplicadas imediatamente",
"wolTip": "Após configurar o WOL (Wake-on-LAN), um pedido de WOL é enviado cada vez que o servidor é conectado.",
"write": "Escrita", "write": "Escrita",
"writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe." "writeScriptFailTip": "Falha ao escrever no script, possivelmente devido à falta de permissões ou o diretório não existe."
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Настройки Webdav пусты", "webdavSettingEmpty": "Настройки Webdav пусты",
"whenOpenApp": "при открытии приложения", "whenOpenApp": "при открытии приложения",
"willTakEeffectImmediately": "Изменения вступят в силу немедленно", "willTakEeffectImmediately": "Изменения вступят в силу немедленно",
"wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.",
"write": "запись", "write": "запись",
"writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует." "writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует."
} }

View File

@@ -323,11 +323,12 @@
"virtKeyHelpIME": "打开/关闭键盘", "virtKeyHelpIME": "打开/关闭键盘",
"virtKeyHelpSFTP": "在 SFTP 中打开当前路径。", "virtKeyHelpSFTP": "在 SFTP 中打开当前路径。",
"waitConnection": "请等待连接建立", "waitConnection": "请等待连接建立",
"wakeLock": "保持醒", "wakeLock": "保持醒",
"watchNotPaired": "没有已配对的 Apple Watch", "watchNotPaired": "没有已配对的 Apple Watch",
"webdavSettingEmpty": "Webdav 设置项为空", "webdavSettingEmpty": "Webdav 设置项为空",
"whenOpenApp": "当打开 App 时", "whenOpenApp": "当打开 App 时",
"willTakEeffectImmediately": "更改将会立即生效", "willTakEeffectImmediately": "更改将会立即生效",
"wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOl 请求",
"write": "写", "write": "写",
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等" "writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等"
} }

View File

@@ -328,6 +328,7 @@
"webdavSettingEmpty": "Webdav 設置項爲空", "webdavSettingEmpty": "Webdav 設置項爲空",
"whenOpenApp": "當打開 App 時", "whenOpenApp": "當打開 App 時",
"willTakEeffectImmediately": "更改將會立即生效", "willTakEeffectImmediately": "更改將會立即生效",
"wolTip": "在配置WOL網絡喚醒每次連接伺服器都會先發送一次WOL請求。",
"write": "写", "write": "写",
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。" "writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。"
} }

View File

@@ -12,6 +12,7 @@ import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/core/utils/ui.dart'; import 'package:toolbox/core/utils/ui.dart';
import 'package:toolbox/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/app/shell_func.dart';
import 'package:toolbox/data/model/server/custom.dart'; import 'package:toolbox/data/model/server/custom.dart';
import 'package:toolbox/data/model/server/wol_cfg.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/view/widget/expand_tile.dart'; import 'package:toolbox/view/widget/expand_tile.dart';
@@ -44,6 +45,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
final _customCmdCtrl = TextEditingController(); final _customCmdCtrl = TextEditingController();
final _preferTempDevCtrl = TextEditingController(); final _preferTempDevCtrl = TextEditingController();
final _logoUrlCtrl = TextEditingController(); final _logoUrlCtrl = TextEditingController();
final _wolMacCtrl = TextEditingController();
final _wolIpCtrl = TextEditingController();
final _wolPwdCtrl = TextEditingController();
final _nameFocus = FocusNode(); final _nameFocus = FocusNode();
final _ipFocus = FocusNode(); final _ipFocus = FocusNode();
@@ -99,6 +103,13 @@ class _ServerEditPageState extends State<ServerEditPage> {
_preferTempDevCtrl.text = custom.preferTempDev ?? ''; _preferTempDevCtrl.text = custom.preferTempDev ?? '';
_logoUrlCtrl.text = custom.logoUrl ?? ''; _logoUrlCtrl.text = custom.logoUrl ?? '';
} }
final wol = spi.wolCfg;
if (wol != null) {
_wolMacCtrl.text = wol.mac;
_wolIpCtrl.text = wol.ip;
_wolPwdCtrl.text = wol.pwd ?? '';
}
} }
} }
@@ -120,6 +131,9 @@ class _ServerEditPageState extends State<ServerEditPage> {
_customCmdCtrl.dispose(); _customCmdCtrl.dispose();
_preferTempDevCtrl.dispose(); _preferTempDevCtrl.dispose();
_logoUrlCtrl.dispose(); _logoUrlCtrl.dispose();
_wolMacCtrl.dispose();
_wolIpCtrl.dispose();
_wolPwdCtrl.dispose();
} }
@override @override
@@ -380,6 +394,27 @@ class _ServerEditPageState extends State<ServerEditPage> {
hint: 'https://example.com/logo.png', hint: 'https://example.com/logo.png',
), ),
UIs.height7, UIs.height7,
..._buildPVEs(),
UIs.height7,
..._buildCustomCmds(),
UIs.height7,
Text(l10n.temperature, style: UIs.text13Grey),
UIs.height7,
Input(
controller: _preferTempDevCtrl,
type: TextInputType.text,
label: l10n.deviceName,
icon: MingCute.low_temperature_line,
hint: 'nvme-pci-0400',
),
UIs.height7,
..._buildWOLs(),
],
);
}
List<Widget> _buildPVEs() {
return [
const Text('PVE', style: UIs.text13Grey), const Text('PVE', style: UIs.text13Grey),
UIs.height7, UIs.height7,
Input( Input(
@@ -406,7 +441,11 @@ class _ServerEditPageState extends State<ServerEditPage> {
), ),
), ),
).card, ).card,
UIs.height7, ];
}
List<Widget> _buildCustomCmds() {
return [
Text(l10n.customCmd, style: UIs.text13Grey), Text(l10n.customCmd, style: UIs.text13Grey),
UIs.height7, UIs.height7,
Input( Input(
@@ -426,18 +465,41 @@ class _ServerEditPageState extends State<ServerEditPage> {
trailing: const Icon(Icons.open_in_new, size: 17), trailing: const Icon(Icons.open_in_new, size: 17),
onTap: () => openUrl(l10n.customCmdDocUrl), onTap: () => openUrl(l10n.customCmdDocUrl),
).card, ).card,
];
}
List<Widget> _buildWOLs() {
return [
const Text('Wake On LAN', style: UIs.text13Grey),
UIs.height7, UIs.height7,
Text(l10n.temperature, style: UIs.text13Grey), ListTile(
UIs.height7, leading: const Icon(BoxIcons.bxs_help_circle),
title: Text(l10n.about),
subtitle: Text(l10n.wolTip, style: UIs.text12Grey),
).card,
Input( Input(
controller: _preferTempDevCtrl, controller: _wolMacCtrl,
type: TextInputType.text, type: TextInputType.text,
label: l10n.deviceName, label: 'Mac ${l10n.addr}',
icon: MingCute.low_temperature_line, icon: Icons.computer,
hint: 'nvme-pci-0400', hint: '00:11:22:33:44:55',
), ),
], Input(
); controller: _wolIpCtrl,
type: TextInputType.text,
label: 'IP ${l10n.addr}',
icon: Icons.network_cell,
hint: '192.168.1.x',
),
Input(
controller: _wolPwdCtrl,
type: TextInputType.text,
obscureText: true,
label: l10n.pwd,
icon: Icons.password,
hint: l10n.pwd,
),
];
} }
Widget _buildFAB() { Widget _buildFAB() {
@@ -544,6 +606,17 @@ class _ServerEditPageState extends State<ServerEditPage> {
logoUrl: _logoUrlCtrl.text.selfIfNotNullEmpty, logoUrl: _logoUrlCtrl.text.selfIfNotNullEmpty,
); );
final wol = WakeOnLanCfg(
mac: _wolMacCtrl.text,
ip: _wolIpCtrl.text,
pwd: _wolPwdCtrl.text.selfIfNotNullEmpty,
);
final wolValidation = wol.validate();
if (!wolValidation.$2) {
context.showSnackBar('${l10n.failed}: ${wolValidation.$1}');
return;
}
final spi = ServerPrivateInfo( final spi = ServerPrivateInfo(
name: _nameController.text.isEmpty name: _nameController.text.isEmpty
? _ipController.text ? _ipController.text
@@ -560,6 +633,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
autoConnect: _autoConnect.value, autoConnect: _autoConnect.value,
jumpId: _jumpServer.value, jumpId: _jumpServer.value,
custom: custom, custom: custom,
wolCfg: wol,
); );
if (widget.spi == null) { if (widget.spi == null) {

View File

@@ -1135,7 +1135,6 @@ class _SettingPageState extends State<SettingPage> {
Widget _buildWakeLock() { Widget _buildWakeLock() {
return ListTile( return ListTile(
leading: const Icon(MingCute.lock_fill),
title: Text(l10n.wakeLock), title: Text(l10n.wakeLock),
trailing: StoreSwitch(prop: _setting.generalWakeLock), trailing: StoreSwitch(prop: _setting.generalWakeLock),
); );

View File

@@ -20,19 +20,20 @@ final class IconTextBtn extends StatelessWidget {
return IconButton( return IconButton(
onPressed: onPressed, onPressed: onPressed,
tooltip: text, tooltip: text,
icon: orientation == Orientation.landscape ? Row( icon: orientation == Orientation.landscape
? Row(
children: [ children: [
Icon(icon), Icon(icon),
UIs.width7, UIs.width7,
Text(text, style: UIs.text13Grey), Text(text, style: UIs.text13Grey),
], ],
) : Column( )
: Column(
children: [ children: [
Icon(icon), Icon(icon),
UIs.height7, UIs.height7,
Text(text, style: UIs.text13Grey), Text(text, style: UIs.text13Grey),
], ],
) ));
);
} }
} }

View File

@@ -471,7 +471,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -481,7 +481,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -608,7 +608,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -618,7 +618,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -638,7 +638,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 884; CURRENT_PROJECT_VERSION = 887;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6; "DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -649,7 +649,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.884; MARKETING_VERSION = 1.0.887;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -1333,6 +1333,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "13.0.0" version: "13.0.0"
wake_on_lan:
dependency: "direct main"
description:
name: wake_on_lan
sha256: a0db43df0cd05181f476f38ec63345a763b7d3b9d8ab25cabbff45881780cb8e
url: "https://pub.dev"
source: hosted
version: "4.1.1+3"
wakelock_plus: wakelock_plus:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -78,6 +78,7 @@ dependencies:
fl_chart: ^0.67.0 fl_chart: ^0.67.0
wakelock_plus: ^1.2.4 wakelock_plus: ^1.2.4
extended_image: ^8.2.0 extended_image: ^8.2.0
wake_on_lan: ^4.1.1+3
dev_dependencies: dev_dependencies:
flutter_native_splash: ^2.1.6 flutter_native_splash: ^2.1.6