rollback: write script to /dev/shm (#474)

This commit is contained in:
lollipopkit🏳️‍⚧️
2024-07-21 17:58:14 +08:00
committed by GitHub
parent b0936c5e6e
commit 255abe8b11
25 changed files with 226 additions and 211 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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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

@@ -19,43 +19,24 @@ enum ShellFunc {
/// srvboxm -> ServerBox Mobile /// srvboxm -> ServerBox Mobile
static const scriptFile = 'srvboxm_v${BuildData.script}.sh'; static const scriptFile = 'srvboxm_v${BuildData.script}.sh';
static const scriptPathShm = '/dev/shm/$scriptFile'; static const scriptDir = '~/.config/server_box';
static const scriptPathHome = '~/.config/server_box/$scriptFile'; static const scriptPath = '$scriptDir/$scriptFile';
static final _scriptPathMap = <String, String>{}; static const String installShellCmd = """
static String getScriptPath(String id) { cat > $scriptPath
return _scriptPathMap.putIfAbsent(id, () => scriptPathShm); chmod 744 $scriptPath
}
static String setScriptPath(String id, String path) {
return _scriptPathMap[id] = path;
}
static String installShellCmd(String id) {
final path = getScriptPath(id);
return """
cat > $path
chmod 744 $path
"""; """;
}
String get flag { String get flag => switch (this) {
switch (this) { ShellFunc.process => 'p',
case ShellFunc.status: ShellFunc.shutdown => 'sd',
return 's'; ShellFunc.reboot => 'r',
// case ShellFunc.docker: ShellFunc.suspend => 'sp',
// return 'd'; ShellFunc.status => 's',
case ShellFunc.process: // ShellFunc.docker=> 'd',
return 'p'; };
case ShellFunc.shutdown:
return 'sd';
case ShellFunc.reboot:
return 'r';
case ShellFunc.suspend:
return 'sp';
}
}
String exec(String id) => 'sh ${getScriptPath(id)} -$flag'; String get exec => 'sh $scriptPath -$flag';
String get name { String get name {
switch (this) { switch (this) {

View File

@@ -315,23 +315,14 @@ class ServerProvider extends ChangeNotifier {
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List; final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
Future<void> fn(String scriptPath) async { try {
await s.client?.runForOutput( await s.client?.runForOutput(
scriptPath, ShellFunc.installShellCmd,
action: (session) async { action: (session) async {
session.stdin.add(scriptRaw); session.stdin.add(scriptRaw);
session.stdin.close(); session.stdin.close();
}, },
); );
ShellFunc.setScriptPath(spi.id, scriptPath);
}
try {
try {
await fn(ShellFunc.scriptPathShm);
} catch (_) {
await fn(ShellFunc.scriptPathHome);
}
} on SSHAuthAbortError catch (e) { } on SSHAuthAbortError catch (e) {
TryLimiter.inc(sid); TryLimiter.inc(sid);
s.status.err = SSHErr(type: SSHErrType.auth, message: e.toString()); s.status.err = SSHErr(type: SSHErrType.auth, message: e.toString());
@@ -343,10 +334,11 @@ class ServerProvider extends ChangeNotifier {
_setServerState(s, ServerConn.failed); _setServerState(s, ServerConn.failed);
return; return;
} catch (e) { } catch (e) {
final err = e.toString();
TryLimiter.inc(sid); TryLimiter.inc(sid);
s.status.err = SSHErr(type: SSHErrType.auth, message: e.toString()); s.status.err = SSHErr(type: SSHErrType.writeScript, message: err);
_setServerState(s, ServerConn.failed); _setServerState(s, ServerConn.failed);
Loggers.app.warning('Write script to ${spi.name} by shell', e); Loggers.app.warning('Write script to ${spi.name} by shell', err);
} }
} }
@@ -363,7 +355,7 @@ class ServerProvider extends ChangeNotifier {
String? raw; String? raw;
try { try {
raw = await s.client?.run(ShellFunc.status.exec(spi.id)).string; raw = await s.client?.run(ShellFunc.status.exec).string;
segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList(); segments = raw?.split(ShellFunc.seperator).map((e) => e.trim()).toList();
if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) { if (raw == null || raw.isEmpty || segments == null || segments.isEmpty) {
if (Stores.setting.keepStatusWhenErr.fetch()) { if (Stores.setting.keepStatusWhenErr.fetch()) {

View File

@@ -2,6 +2,6 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 1018; static const int build = 1021;
static const int script = 51; static const int script = 53;
} }

View File

@@ -340,5 +340,6 @@
"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.", "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.",
"writeScriptTip": "Nach der Verbindung mit dem Server wird ein Skript in ~/.config/server_box geschrieben, um den Systemstatus zu überwachen. Sie können den Skriptinhalt überprüfen."
} }

View File

@@ -340,5 +340,6 @@
"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.", "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.",
"writeScriptTip": "After connecting to the server, a script will be written to ~/.config/server_box to monitor the system status. You can review the script content."
} }

View File

@@ -340,5 +340,6 @@
"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.", "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.",
"writeScriptTip": "Después de conectarse al servidor, se escribirá un script en ~/.config/server_box para monitorear el estado del sistema. Puedes revisar el contenido del script."
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "Prendra effet immédiatement", "willTakEeffectImmediately": "Prendra effet immédiatement",
"wolTip": "Après avoir configuré le WOL (Wake-on-LAN), une requête WOL est envoyée chaque fois que le serveur est connecté.", "wolTip": "Après avoir configuré le WOL (Wake-on-LAN), une requête WOL est envoyée chaque fois que le serveur est connecté.",
"write": "Écrire", "write": "Écrire",
"writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas." "writeScriptFailTip": "Échec de l'écriture dans le script, probablement en raison d'un manque de permissions ou que le répertoire n'existe pas.",
"writeScriptTip": "Après la connexion au serveur, un script sera écrit dans ~/.config/server_box pour surveiller létat du système. Vous pouvez examiner le contenu du script."
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "Akan segera berlaku", "willTakEeffectImmediately": "Akan segera berlaku",
"wolTip": "Setelah mengonfigurasi WOL (Wake-on-LAN), permintaan WOL dikirim setiap kali server terhubung.", "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.",
"writeScriptTip": "Setelah terhubung ke server, sebuah skrip akan ditulis ke ~/.config/server_box untuk memantau status sistem. Anda dapat meninjau konten skrip tersebut."
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "変更は即座に有効になります", "willTakEeffectImmediately": "変更は即座に有効になります",
"wolTip": "WOLWake-on-LANを設定した後、サーバーに接続するたびにWOLリクエストが送信されます。", "wolTip": "WOLWake-on-LANを設定した後、サーバーに接続するたびにWOLリクエストが送信されます。",
"write": "書き込み", "write": "書き込み",
"writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。" "writeScriptFailTip": "スクリプトの書き込みに失敗しました。権限がないかディレクトリが存在しない可能性があります。",
"writeScriptTip": "サーバーに接続すると、システムの状態を監視するためのスクリプトが ~/.config/server_box に書き込まれます。スクリプトの内容を確認できます。"
} }

View File

@@ -340,5 +340,6 @@
"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.", "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.",
"writeScriptTip": "Na het verbinden met de server wordt een script geschreven naar ~/.config/server_box om de systeemstatus te monitoren. U kunt de inhoud van het script controleren."
} }

View File

@@ -340,5 +340,6 @@
"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.", "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.",
"writeScriptTip": "Após conectar ao servidor, um script será escrito em ~/.config/server_box para monitorar o status do sistema. Você pode revisar o conteúdo do script."
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "Изменения вступят в силу немедленно", "willTakEeffectImmediately": "Изменения вступят в силу немедленно",
"wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.", "wolTip": "После настройки WOL (Wake-on-LAN) при каждом подключении к серверу отправляется запрос WOL.",
"write": "запись", "write": "запись",
"writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует." "writeScriptFailTip": "Запись в скрипт не удалась, возможно, из-за отсутствия прав или директории не существует.",
"writeScriptTip": "После подключения к серверу скрипт будет записан в ~/.config/server_box для мониторинга состояния системы. Вы можете проверить содержимое скрипта."
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "更改将会立即生效", "willTakEeffectImmediately": "更改将会立即生效",
"wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOL 请求", "wolTip": "在配置 WOL 后,每次连接服务器都会先发送一次 WOL 请求",
"write": "写", "write": "写",
"writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等" "writeScriptFailTip": "写入脚本失败,可能是没有权限/目录不存在等",
"writeScriptTip": "在连接服务器后,会向 ~/.config/server_box 写入脚本来监测系统状态,你可以审查脚本内容。"
} }

View File

@@ -340,5 +340,6 @@
"willTakEeffectImmediately": "更改將會立即生效", "willTakEeffectImmediately": "更改將會立即生效",
"wolTip": "在配置 WOL網絡喚醒每次連接伺服器都會先發送一次 WOL 請求。", "wolTip": "在配置 WOL網絡喚醒每次連接伺服器都會先發送一次 WOL 請求。",
"write": "写", "write": "写",
"writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。" "writeScriptFailTip": "寫入腳本失敗,可能是沒有權限/目錄不存在等。",
"writeScriptTip": "連接到伺服器後,將會在 ~/.config/server_box 中寫入一個腳本來監測系統狀態。你可以審查腳本內容。"
} }

View File

@@ -9,7 +9,6 @@ import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:server_box/app.dart'; import 'package:server_box/app.dart';
import 'package:server_box/core/utils/sync/icloud.dart'; import 'package:server_box/core/utils/sync/icloud.dart';
import 'package:server_box/core/utils/sync/webdav.dart'; import 'package:server_box/core/utils/sync/webdav.dart';
@@ -95,11 +94,7 @@ Future<void> _initData() async {
Hive.registerAdapter(ServerCustomAdapter()); // 7 Hive.registerAdapter(ServerCustomAdapter()); // 7
Hive.registerAdapter(WakeOnLanCfgAdapter()); // 8 Hive.registerAdapter(WakeOnLanCfgAdapter()); // 8
try { await SharedPref.init(); // Call this before accessing any store
/// Apps' data on other platforms are stored in a container that prevents
/// access by other apps. Therefore, there is no need to encrypt the data.
if (isLinux || isWindows) await SecureStore.init();
} catch (_) {}
await Stores.setting.init(); await Stores.setting.init();
await Stores.server.init(); await Stores.server.init();
@@ -127,8 +122,6 @@ void _setupDebug() {
void _doPlatformRelated() async { void _doPlatformRelated() async {
if (isAndroid) { if (isAndroid) {
// SharedPreferences is only used on Android for saving home widgets settings.
SharedPreferences.setPrefix('');
// try switch to highest refresh rate // try switch to highest refresh rate
FlutterDisplayMode.setHighRefreshRate(); FlutterDisplayMode.setHighRefreshRate();
} }

View File

@@ -305,6 +305,8 @@ class BackupPage extends StatelessWidget {
final url = TextEditingController(text: Stores.setting.webdavUrl.fetch()); final url = TextEditingController(text: Stores.setting.webdavUrl.fetch());
final user = TextEditingController(text: Stores.setting.webdavUser.fetch()); final user = TextEditingController(text: Stores.setting.webdavUser.fetch());
final pwd = TextEditingController(text: Stores.setting.webdavPwd.fetch()); final pwd = TextEditingController(text: Stores.setting.webdavPwd.fetch());
final nodeUser = FocusNode();
final nodePwd = FocusNode();
final result = await context.showRoundDialog<bool>( final result = await context.showRoundDialog<bool>(
title: 'WebDAV', title: 'WebDAV',
child: Column( child: Column(
@@ -314,14 +316,19 @@ class BackupPage extends StatelessWidget {
label: 'URL', label: 'URL',
hint: 'https://example.com/webdav/', hint: 'https://example.com/webdav/',
controller: url, controller: url,
onSubmitted: (p0) => FocusScope.of(context).requestFocus(nodeUser),
), ),
Input( Input(
label: l10n.user, label: l10n.user,
controller: user, controller: user,
node: nodeUser,
onSubmitted: (p0) => FocusScope.of(context).requestFocus(nodePwd),
), ),
Input( Input(
label: l10n.pwd, label: l10n.pwd,
controller: pwd, controller: pwd,
node: nodePwd,
onSubmitted: (_) => context.pop(true),
), ),
], ],
), ),

View File

@@ -118,32 +118,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
Widget _buildFAB() { Widget _buildFAB() {
return FloatingActionButton( return FloatingActionButton(
tooltip: l10n.save, tooltip: l10n.save,
onPressed: () async { onPressed: _onTapSave,
final name = _nameController.text;
final key = _standardizeLineSeparators(_keyController.text.trim());
final pwd = _pwdController.text;
if (name.isEmpty || key.isEmpty) {
context.showSnackBar(l10n.fieldMustNotEmpty);
return;
}
FocusScope.of(context).unfocus();
_loading.value = UIs.centerSizedLoading;
try {
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted);
if (widget.pki != null) {
Pros.key.update(widget.pki!, pki);
} else {
Pros.key.add(pki);
}
} catch (e) {
context.showSnackBar(e.toString());
rethrow;
} finally {
_loading.value = null;
}
context.pop();
},
child: const Icon(Icons.save), child: const Icon(Icons.save),
); );
} }
@@ -206,6 +181,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
obscureText: true, obscureText: true,
label: l10n.pwd, label: l10n.pwd,
icon: Icons.password, icon: Icons.password,
onSubmitted: (_) => _onTapSave(),
), ),
SizedBox(height: MediaQuery.of(context).size.height * 0.1), SizedBox(height: MediaQuery.of(context).size.height * 0.1),
ValBuilder( ValBuilder(
@@ -215,4 +191,31 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
], ],
); );
} }
void _onTapSave() async {
final name = _nameController.text;
final key = _standardizeLineSeparators(_keyController.text.trim());
final pwd = _pwdController.text;
if (name.isEmpty || key.isEmpty) {
context.showSnackBar(l10n.fieldMustNotEmpty);
return;
}
FocusScope.of(context).unfocus();
_loading.value = UIs.centerSizedLoading;
try {
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted);
if (widget.pki != null) {
Pros.key.update(widget.pki!, pki);
} else {
Pros.key.add(pki);
}
} catch (e) {
context.showSnackBar(e.toString());
rethrow;
} finally {
_loading.value = null;
}
context.pop();
}
} }

View File

@@ -52,7 +52,7 @@ class _ProcessPageState extends State<ProcessPage> {
Future<void> _refresh() async { Future<void> _refresh() async {
if (mounted) { if (mounted) {
final result = final result =
await _client?.run(ShellFunc.process.exec(widget.spi.id)).string; await _client?.run(ShellFunc.process.exec).string;
if (result == null || result.isEmpty) { if (result == null || result.isEmpty) {
context.showSnackBar(l10n.noResult); context.showSnackBar(l10n.noResult);
return; return;

View File

@@ -174,6 +174,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
Widget _buildForm() { Widget _buildForm() {
final children = [ final children = [
_buildWriteScriptTip(),
Input( Input(
autoFocus: true, autoFocus: true,
controller: _nameController, controller: _nameController,
@@ -638,4 +639,19 @@ class _ServerEditPageState extends State<ServerEditPage> {
context.pop(); context.pop();
} }
Widget _buildWriteScriptTip() {
return ListTile(
leading: const Icon(Icons.tips_and_updates).paddingOnly(left: 13),
title: Text(l10n.attention),
onTap: () {
context.showRoundDialog(
title: l10n.attention,
child: SimpleMarkdown(data: l10n.writeScriptTip),
actions: Btns.oks(onTap: () => context.pop(true)),
);
},
trailing: const Icon(Icons.keyboard_arrow_right),
).cardx;
}
} }

View File

@@ -356,7 +356,7 @@ class _ServerPageState extends State<ServerPage>
Stores.setting.showSuspendTip.put(false); Stores.setting.showSuspendTip.put(false);
} }
srv.client?.execWithPwd( srv.client?.execWithPwd(
ShellFunc.suspend.exec(srv.id), ShellFunc.suspend.exec,
context: context, context: context,
id: srv.id, id: srv.id,
); );
@@ -370,7 +370,7 @@ class _ServerPageState extends State<ServerPage>
IconTextBtn( IconTextBtn(
onPressed: () => _askFor( onPressed: () => _askFor(
func: () => srv.client?.execWithPwd( func: () => srv.client?.execWithPwd(
ShellFunc.shutdown.exec(srv.id), ShellFunc.shutdown.exec,
context: context, context: context,
id: srv.id, id: srv.id,
), ),
@@ -383,7 +383,7 @@ class _ServerPageState extends State<ServerPage>
IconTextBtn( IconTextBtn(
onPressed: () => _askFor( onPressed: () => _askFor(
func: () => srv.client?.execWithPwd( func: () => srv.client?.execWithPwd(
ShellFunc.reboot.exec(srv.id), ShellFunc.reboot.exec,
context: context, context: context,
id: srv.id, id: srv.id,
), ),

View File

@@ -581,21 +581,8 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
void _mkdir() { void _mkdir() {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
context.showRoundDialog(
title: l10n.createFolder, void onSubmitted() async {
child: Input(
autoFocus: true,
icon: Icons.folder,
controller: textController,
label: l10n.name,
),
actions: [
TextButton(
onPressed: () => context.pop(),
child: Text(l10n.cancel),
),
TextButton(
onPressed: () async {
if (textController.text.isEmpty) { if (textController.text.isEmpty) {
context.showRoundDialog( context.showRoundDialog(
child: Text(l10n.fieldMustNotEmpty), child: Text(l10n.fieldMustNotEmpty),
@@ -621,7 +608,24 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} catch (e, s) { } catch (e, s) {
context.showErrDialog(e: e, s: s, operation: l10n.createFolder); context.showErrDialog(e: e, s: s, operation: l10n.createFolder);
} }
}, }
context.showRoundDialog(
title: l10n.createFolder,
child: Input(
autoFocus: true,
icon: Icons.folder,
controller: textController,
label: l10n.name,
onSubmitted: (_) => onSubmitted(),
),
actions: [
TextButton(
onPressed: () => context.pop(),
child: Text(l10n.cancel),
),
TextButton(
onPressed: onSubmitted,
child: Text(l10n.ok, style: UIs.textRed), child: Text(l10n.ok, style: UIs.textRed),
), ),
], ],
@@ -631,17 +635,8 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
void _newFile() { void _newFile() {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
context.showRoundDialog(
title: l10n.createFile, void onSubmitted() async {
child: Input(
autoFocus: true,
icon: Icons.insert_drive_file,
controller: textController,
label: l10n.name,
),
actions: [
TextButton(
onPressed: () async {
if (textController.text.isEmpty) { if (textController.text.isEmpty) {
context.showRoundDialog( context.showRoundDialog(
title: l10n.attention, title: l10n.attention,
@@ -668,7 +663,20 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} catch (e, s) { } catch (e, s) {
context.showErrDialog(e: e, s: s, operation: l10n.createFile); context.showErrDialog(e: e, s: s, operation: l10n.createFile);
} }
}, }
context.showRoundDialog(
title: l10n.createFile,
child: Input(
autoFocus: true,
icon: Icons.insert_drive_file,
controller: textController,
label: l10n.name,
onSubmitted: (_) => onSubmitted(),
),
actions: [
TextButton(
onPressed: onSubmitted,
child: Text(l10n.ok, style: UIs.textRed), child: Text(l10n.ok, style: UIs.textRed),
), ),
], ],
@@ -678,18 +686,8 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
void _rename(SftpName file) { void _rename(SftpName file) {
context.pop(); context.pop();
final textController = TextEditingController(text: file.filename); final textController = TextEditingController(text: file.filename);
context.showRoundDialog(
title: l10n.rename, void onSubmitted() async {
child: Input(
autoFocus: true,
icon: Icons.abc,
controller: textController,
label: l10n.name,
),
actions: [
TextButton(onPressed: () => context.pop(), child: Text(l10n.cancel)),
TextButton(
onPressed: () async {
if (textController.text.isEmpty) { if (textController.text.isEmpty) {
context.showRoundDialog( context.showRoundDialog(
title: l10n.attention, title: l10n.attention,
@@ -716,7 +714,21 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} catch (e, s) { } catch (e, s) {
context.showErrDialog(e: e, s: s, operation: l10n.rename); context.showErrDialog(e: e, s: s, operation: l10n.rename);
} }
}, }
context.showRoundDialog(
title: l10n.rename,
child: Input(
autoFocus: true,
icon: Icons.abc,
controller: textController,
label: l10n.name,
onSubmitted: (_) => onSubmitted(),
),
actions: [
TextButton(onPressed: () => context.pop(), child: Text(l10n.cancel)),
TextButton(
onPressed: onSubmitted,
child: Text(l10n.rename, style: UIs.textRed), child: Text(l10n.rename, style: UIs.textRed),
), ),
], ],

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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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 = 1018; CURRENT_PROJECT_VERSION = 1021;
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.1018; MARKETING_VERSION = 1.0.1021;
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

@@ -385,8 +385,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "v1.0.74" ref: "v1.0.77"
resolved-ref: "42000d49fd62ee202a4b37d0a97c29f77c83d0d8" resolved-ref: "57c4c0187673ea9c38b39889ac467be4a8bbddba"
url: "https://github.com/lppcg/fl_lib" url: "https://github.com/lppcg/fl_lib"
source: git source: git
version: "0.0.1" version: "0.0.1"

View File

@@ -1,7 +1,7 @@
name: server_box name: server_box
description: server status & toolbox app. description: server status & toolbox app.
publish_to: 'none' publish_to: 'none'
version: 1.0.1018+1018 version: 1.0.1021+1021
environment: environment:
sdk: ">=3.0.0" sdk: ">=3.0.0"
@@ -61,11 +61,11 @@ dependencies:
fl_lib: fl_lib:
git: git:
url: https://github.com/lppcg/fl_lib url: https://github.com/lppcg/fl_lib
ref: v1.0.74 ref: v1.0.77
dependency_overrides: dependency_overrides:
# dartssh2: # dartssh2:
# path: ../dartssh2 # path: ../dartssh2
# fl_lib: # fl_lib:
# path: ../fl_lib # path: ../fl_lib
# xterm: # xterm: