translate backend strings

This commit is contained in:
Aiden McClelland
2026-01-16 17:03:34 -07:00
parent 763c7d9f87
commit d786424353
96 changed files with 5177 additions and 775 deletions

65
core/Cargo.lock generated
View File

@@ -1177,9 +1177,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.52" version = "1.2.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -2726,9 +2726,9 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]] [[package]]
name = "fixed-capacity-vec" name = "fixed-capacity-vec"
@@ -4186,9 +4186,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.83" version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -6587,7 +6587,7 @@ dependencies = [
[[package]] [[package]]
name = "rpc-toolkit" name = "rpc-toolkit"
version = "0.3.2" version = "0.3.2"
source = "git+https://github.com/Start9Labs/rpc-toolkit.git#406ee9e88bf20e3155f150eb755b5b9c2aefd167" source = "git+https://github.com/Start9Labs/rpc-toolkit.git#39e547ff99d997c19f9b6483b28a4394ca5a07bc"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"async-trait", "async-trait",
@@ -6727,9 +6727,9 @@ dependencies = [
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.26" version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
@@ -6806,7 +6806,7 @@ dependencies = [
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.103.8", "rustls-webpki 0.103.9",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
@@ -6834,9 +6834,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.13.2" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [ dependencies = [
"web-time", "web-time",
"zeroize", "zeroize",
@@ -6856,7 +6856,7 @@ dependencies = [
"rustls 0.23.36", "rustls 0.23.36",
"rustls-native-certs", "rustls-native-certs",
"rustls-platform-verifier-android", "rustls-platform-verifier-android",
"rustls-webpki 0.103.8", "rustls-webpki 0.103.9",
"security-framework 3.5.1", "security-framework 3.5.1",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
@@ -6882,9 +6882,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.8" version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"ring", "ring",
@@ -9556,7 +9556,7 @@ dependencies = [
"paste", "paste",
"pin-project", "pin-project",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.103.8", "rustls-webpki 0.103.9",
"thiserror 2.0.17", "thiserror 2.0.17",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -10201,9 +10201,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasip2" name = "wasip2"
version = "1.0.1+wasi-0.2.4" version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
@@ -10225,9 +10225,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.106" version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@@ -10238,11 +10238,12 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.56" version = "0.4.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util",
"js-sys", "js-sys",
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -10251,9 +10252,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.106" version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -10261,9 +10262,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.106" version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
@@ -10274,9 +10275,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.106" version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -10375,9 +10376,9 @@ checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.83" version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -10974,9 +10975,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.46.0" version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]] [[package]]
name = "writeable" name = "writeable"

4096
core/locales/i18n.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
"get-input", "get-input",
from_fn_async(get_action_input) from_fn_async(get_action_input)
.with_display_serializable() .with_display_serializable()
.with_about("Get action input spec") .with_about("about.get-action-input-spec")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -36,14 +36,14 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
} }
Ok(()) Ok(())
}) })
.with_about("Run service action") .with_about("about.run-service-action")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"clear-task", "clear-task",
from_fn_async(clear_task) from_fn_async(clear_task)
.no_display() .no_display()
.with_about("Clear a service task") .with_about("about.clear-service-task")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -51,7 +51,10 @@ pub async fn write_shadow(password: &str) -> Result<(), Error> {
match line.split_once(":") { match line.split_once(":") {
Some((user, rest)) if user == "start9" || user == "kiosk" => { Some((user, rest)) if user == "start9" || user == "kiosk" => {
let (_, rest) = rest.split_once(":").ok_or_else(|| { let (_, rest) = rest.split_once(":").ok_or_else(|| {
Error::new(eyre!("malformed /etc/shadow"), ErrorKind::ParseSysInfo) Error::new(
eyre!("{}", t!("auth.malformed-etc-shadow")),
ErrorKind::ParseSysInfo,
)
})?; })?;
shadow_file shadow_file
.write_all(format!("{user}:{hash}:{rest}\n").as_bytes()) .write_all(format!("{user}:{hash}:{rest}\n").as_bytes())
@@ -81,7 +84,7 @@ impl PasswordType {
PasswordType::String(x) => Ok(x), PasswordType::String(x) => Ok(x),
PasswordType::EncryptedWire(x) => x.decrypt(current_secret).ok_or_else(|| { PasswordType::EncryptedWire(x) => x.decrypt(current_secret).ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Couldn't decode password"), color_eyre::eyre::eyre!("{}", t!("auth.couldnt-decode-password")),
crate::ErrorKind::Unknown, crate::ErrorKind::Unknown,
) )
}), }),
@@ -125,19 +128,19 @@ where
"login", "login",
from_fn_async(cli_login::<AC>) from_fn_async(cli_login::<AC>)
.no_display() .no_display()
.with_about("Log in a new auth session"), .with_about("about.login-new-auth-session"),
) )
.subcommand( .subcommand(
"logout", "logout",
from_fn_async(logout::<AC>) from_fn_async(logout::<AC>)
.with_metadata("get_session", Value::Bool(true)) .with_metadata("get_session", Value::Bool(true))
.no_display() .no_display()
.with_about("Log out of current auth session") .with_about("about.logout-current-auth-session")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"session", "session",
session::<C, AC>().with_about("List or kill auth sessions"), session::<C, AC>().with_about("about.list-or-kill-auth-sessions"),
) )
.subcommand( .subcommand(
"reset-password", "reset-password",
@@ -147,14 +150,14 @@ where
"reset-password", "reset-password",
from_fn_async(cli_reset_password) from_fn_async(cli_reset_password)
.no_display() .no_display()
.with_about("Reset password"), .with_about("about.reset-password"),
) )
.subcommand( .subcommand(
"get-pubkey", "get-pubkey",
from_fn_async(get_pubkey) from_fn_async(get_pubkey)
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.no_display() .no_display()
.with_about("Get public key derived from server private key") .with_about("about.get-pubkey-from-server")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -208,12 +211,12 @@ pub fn check_password(hash: &str, password: &str) -> Result<(), Error> {
ensure_code!( ensure_code!(
argon2::verify_encoded(&hash, password.as_bytes()).map_err(|_| { argon2::verify_encoded(&hash, password.as_bytes()).map_err(|_| {
Error::new( Error::new(
eyre!("Password Incorrect"), eyre!("{}", t!("auth.password-incorrect")),
crate::ErrorKind::IncorrectPassword, crate::ErrorKind::IncorrectPassword,
) )
})?, })?,
crate::ErrorKind::IncorrectPassword, crate::ErrorKind::IncorrectPassword,
"Password Incorrect" t!("auth.password-incorrect")
); );
Ok(()) Ok(())
} }
@@ -327,14 +330,14 @@ where
.with_metadata("get_session", Value::Bool(true)) .with_metadata("get_session", Value::Bool(true))
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_sessions(handle.params, result)) .with_custom_display_fn(|handle, result| display_sessions(handle.params, result))
.with_about("Display all auth sessions") .with_about("about.display-all-auth-sessions")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"kill", "kill",
from_fn_async(kill::<AC>) from_fn_async(kill::<AC>)
.no_display() .no_display()
.with_about("Terminate existing auth session(s)") .with_about("about.terminate-auth-sessions")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -447,13 +450,13 @@ async fn cli_reset_password(
.. ..
}: HandlerArgs<CliContext>, }: HandlerArgs<CliContext>,
) -> Result<(), RpcError> { ) -> Result<(), RpcError> {
let old_password = rpassword::prompt_password("Current Password: ")?; let old_password = rpassword::prompt_password(&t!("auth.prompt-current-password"))?;
let new_password = { let new_password = {
let new_password = rpassword::prompt_password("New Password: ")?; let new_password = rpassword::prompt_password(&t!("auth.prompt-new-password"))?;
if new_password != rpassword::prompt_password("Confirm: ")? { if new_password != rpassword::prompt_password(&t!("auth.prompt-confirm"))? {
return Err(Error::new( return Err(Error::new(
eyre!("Passwords do not match"), eyre!("{}", t!("auth.passwords-do-not-match")),
crate::ErrorKind::IncorrectPassword, crate::ErrorKind::IncorrectPassword,
) )
.into()); .into());
@@ -486,7 +489,7 @@ pub async fn reset_password_impl(
.with_kind(crate::ErrorKind::IncorrectPassword)? .with_kind(crate::ErrorKind::IncorrectPassword)?
{ {
return Err(Error::new( return Err(Error::new(
eyre!("Incorrect Password"), eyre!("{}", t!("auth.password-incorrect")),
crate::ErrorKind::IncorrectPassword, crate::ErrorKind::IncorrectPassword,
)); ));
} }

View File

@@ -69,8 +69,8 @@ impl BackupStatusGuard {
db, db,
None, None,
NotificationLevel::Success, NotificationLevel::Success,
"Backup Complete".to_owned(), t!("backup.bulk.complete-title").to_string(),
"Your backup has completed".to_owned(), t!("backup.bulk.complete-message").to_string(),
BackupReport { BackupReport {
server: ServerBackupReport { server: ServerBackupReport {
attempted: true, attempted: true,
@@ -88,9 +88,8 @@ impl BackupStatusGuard {
db, db,
None, None,
NotificationLevel::Warning, NotificationLevel::Warning,
"Backup Complete".to_owned(), t!("backup.bulk.complete-title").to_string(),
"Your backup has completed, but some package(s) failed to backup" t!("backup.bulk.complete-with-failures").to_string(),
.to_owned(),
BackupReport { BackupReport {
server: ServerBackupReport { server: ServerBackupReport {
attempted: true, attempted: true,
@@ -103,7 +102,7 @@ impl BackupStatusGuard {
.await .await
} }
Err(e) => { Err(e) => {
tracing::error!("Backup Failed: {}", e); tracing::error!("{}", t!("backup.bulk.failed-error", error = e));
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
let err_string = e.to_string(); let err_string = e.to_string();
db.mutate(|db| { db.mutate(|db| {
@@ -111,8 +110,8 @@ impl BackupStatusGuard {
db, db,
None, None,
NotificationLevel::Error, NotificationLevel::Error,
"Backup Failed".to_owned(), t!("backup.bulk.failed-title").to_string(),
"Your backup failed to complete.".to_owned(), t!("backup.bulk.failed-message").to_string(),
BackupReport { BackupReport {
server: ServerBackupReport { server: ServerBackupReport {
attempted: true, attempted: true,
@@ -224,7 +223,7 @@ fn assure_backing_up<'a>(
.as_backup_progress_mut(); .as_backup_progress_mut();
if backing_up.transpose_ref().is_some() { if backing_up.transpose_ref().is_some() {
return Err(Error::new( return Err(Error::new(
eyre!("Server is already backing up!"), eyre!("{}", t!("backup.bulk.already-backing-up")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -303,7 +302,7 @@ async fn perform_backup(
let mut backup_guard = Arc::try_unwrap(backup_guard).map_err(|_| { let mut backup_guard = Arc::try_unwrap(backup_guard).map_err(|_| {
Error::new( Error::new(
eyre!("leaked reference to BackupMountGuard"), eyre!("{}", t!("backup.bulk.leaked-reference")),
ErrorKind::Incoherent, ErrorKind::Incoherent,
) )
})?; })?;

View File

@@ -37,12 +37,12 @@ pub fn backup<C: Context>() -> ParentHandler<C> {
"create", "create",
from_fn_async(backup_bulk::backup_all) from_fn_async(backup_bulk::backup_all)
.no_display() .no_display()
.with_about("Create backup for all packages") .with_about("about.create-backup-all-packages")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"target", "target",
target::target::<C>().with_about("Commands related to a backup target"), target::target::<C>().with_about("about.commands-backup-target"),
) )
} }
@@ -51,7 +51,7 @@ pub fn package_backup<C: Context>() -> ParentHandler<C> {
"restore", "restore",
from_fn_async(restore::restore_packages_rpc) from_fn_async(restore::restore_packages_rpc)
.no_display() .no_display()
.with_about("Restore package(s) from backup") .with_about("about.restore-packages-from-backup")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -63,7 +63,7 @@ pub async fn restore_packages_rpc(
match async { res.await?.await }.await { match async { res.await?.await }.await {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
tracing::error!("Error restoring package {}: {}", id, err); tracing::error!("{}", t!("backup.restore.package-error", id = id, error = err));
tracing::debug!("{:?}", err); tracing::debug!("{:?}", err);
} }
} }
@@ -147,7 +147,7 @@ pub async fn recover_full_server(
match async { res.await?.await }.await { match async { res.await?.await }.await {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
tracing::error!("Error restoring package {}: {}", id, err); tracing::error!("{}", t!("backup.restore.package-error", id = id, error = err));
tracing::debug!("{:?}", err); tracing::debug!("{:?}", err);
} }
} }

View File

@@ -52,21 +52,21 @@ pub fn cifs<C: Context>() -> ParentHandler<C> {
"add", "add",
from_fn_async(add) from_fn_async(add)
.no_display() .no_display()
.with_about("Add a new backup target") .with_about("about.add-new-backup-target")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"update", "update",
from_fn_async(update) from_fn_async(update)
.no_display() .no_display()
.with_about("Update an existing backup target") .with_about("about.update-existing-backup-target")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove) from_fn_async(remove)
.no_display() .no_display()
.with_about("Remove an existing backup target") .with_about("about.remove-existing-backup-target")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -151,7 +151,7 @@ pub async fn update(
id id
} else { } else {
return Err(Error::new( return Err(Error::new(
eyre!("Backup Target ID {} Not Found", id), eyre!("{}", t!("backup.target.cifs.target-not-found", id = id)),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
}; };
@@ -171,7 +171,7 @@ pub async fn update(
.as_idx_mut(&id) .as_idx_mut(&id)
.ok_or_else(|| { .ok_or_else(|| {
Error::new( Error::new(
eyre!("Backup Target ID {} Not Found", BackupTargetId::Cifs { id }), eyre!("{}", t!("backup.target.cifs.target-not-found", id = BackupTargetId::Cifs { id })),
ErrorKind::NotFound, ErrorKind::NotFound,
) )
})? })?
@@ -203,7 +203,7 @@ pub async fn remove(ctx: RpcContext, RemoveParams { id }: RemoveParams) -> Resul
id id
} else { } else {
return Err(Error::new( return Err(Error::new(
eyre!("Backup Target ID {} Not Found", id), eyre!("{}", t!("backup.target.cifs.target-not-found", id = id)),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
}; };
@@ -220,7 +220,7 @@ pub fn load(db: &DatabaseModel, id: u32) -> Result<Cifs, Error> {
.as_idx(&id) .as_idx(&id)
.ok_or_else(|| { .ok_or_else(|| {
Error::new( Error::new(
eyre!("Backup Target ID {} Not Found", id), eyre!("{}", t!("backup.target.cifs.target-not-found-id", id = id)),
ErrorKind::NotFound, ErrorKind::NotFound,
) )
})? })?

View File

@@ -143,13 +143,13 @@ pub fn target<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
.subcommand( .subcommand(
"cifs", "cifs",
cifs::cifs::<C>().with_about("Add, remove, or update a backup target"), cifs::cifs::<C>().with_about("about.add-remove-update-backup-target"),
) )
.subcommand( .subcommand(
"list", "list",
from_fn_async(list) from_fn_async(list)
.with_display_serializable() .with_display_serializable()
.with_about("List existing backup targets") .with_about("about.list-existing-backup-targets")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -159,20 +159,20 @@ pub fn target<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn::<CliContext, _>(|params, info| { .with_custom_display_fn::<CliContext, _>(|params, info| {
display_backup_info(params.params, info) display_backup_info(params.params, info)
}) })
.with_about("Display package backup information") .with_about("about.display-package-backup-information")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"mount", "mount",
from_fn_async(mount) from_fn_async(mount)
.with_about("Mount backup target") .with_about("about.mount-backup-target")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"umount", "umount",
from_fn_async(umount) from_fn_async(umount)
.no_display() .no_display()
.with_about("Unmount backup target") .with_about("about.unmount-backup-target")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -17,6 +17,7 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
|cfg: ContainerClientConfig| Ok(ContainerCliContext::init(cfg)), |cfg: ContainerClientConfig| Ok(ContainerCliContext::init(cfg)),
crate::service::effects::handler(), crate::service::effects::handler(),
) )
.mutate_command(super::translate_cli)
.run(args) .run(args)
{ {
match e.data { match e.data {

View File

@@ -1,9 +1,14 @@
use rust_i18n::t;
pub fn renamed(old: &str, new: &str) -> ! { pub fn renamed(old: &str, new: &str) -> ! {
eprintln!("{old} has been renamed to {new}"); eprintln!(
"{}",
t!("bins.deprecated.renamed", old = old, new = new)
);
std::process::exit(1) std::process::exit(1)
} }
pub fn removed(name: &str) -> ! { pub fn removed(name: &str) -> ! {
eprintln!("{name} has been removed"); eprintln!("{}", t!("bins.deprecated.removed", name = name));
std::process::exit(1) std::process::exit(1)
} }

View File

@@ -2,6 +2,8 @@ use std::collections::{BTreeMap, VecDeque};
use std::ffi::OsString; use std::ffi::OsString;
use std::path::Path; use std::path::Path;
use rust_i18n::t;
pub mod container_cli; pub mod container_cli;
pub mod deprecated; pub mod deprecated;
pub mod registry; pub mod registry;
@@ -10,6 +12,72 @@ pub mod start_init;
pub mod startd; pub mod startd;
pub mod tunnel; pub mod tunnel;
pub fn set_locale() {
let lang = std::env::var("LANG").ok();
let lang = lang
.as_deref()
.map_or("C", |l| l.strip_suffix(".UTF-8").unwrap_or(l));
let mut set_lang = lang;
for l in rust_i18n::available_locales!() {
if l == lang {
set_lang = l;
break;
}
if l.split("_").next().unwrap() == lang.split("_").next().unwrap() {
set_lang = l;
}
}
rust_i18n::set_locale(set_lang);
}
pub fn translate_cli(mut cmd: clap::Command) -> clap::Command {
fn translate(s: impl std::fmt::Display) -> String {
t!(s.to_string()).into_owned()
}
if let Some(s) = cmd.get_about() {
let s = translate(s);
cmd = cmd.about(s);
}
if let Some(s) = cmd.get_long_about() {
let s = translate(s);
cmd = cmd.long_about(s);
}
if let Some(s) = cmd.get_before_help() {
let s = translate(s);
cmd = cmd.before_help(s);
}
if let Some(s) = cmd.get_before_long_help() {
let s = translate(s);
cmd = cmd.before_long_help(s);
}
if let Some(s) = cmd.get_after_help() {
let s = translate(s);
cmd = cmd.after_help(s);
}
if let Some(s) = cmd.get_after_long_help() {
let s = translate(s);
cmd = cmd.after_long_help(s);
}
let args = cmd.get_arguments().cloned().collect::<Vec<_>>();
for mut arg in args {
if let Some(s) = arg.get_help() {
let s = translate(s);
arg = arg.help(s);
}
if let Some(s) = arg.get_long_help() {
let s = translate(s);
arg = arg.long_help(s);
}
cmd = cmd.arg(arg);
}
for cmd in cmd.get_subcommands_mut() {
*cmd = translate_cli(cmd.clone());
}
cmd
}
#[derive(Default)] #[derive(Default)]
pub struct MultiExecutable { pub struct MultiExecutable {
default: Option<&'static str>, default: Option<&'static str>,
@@ -58,7 +126,7 @@ impl MultiExecutable {
if let Some((name, _)) = self.bins.get_key_value(name) { if let Some((name, _)) = self.bins.get_key_value(name) {
self.default = Some(*name); self.default = Some(*name);
} else { } else {
panic!("{name} does not exist in MultiExecutable"); panic!("{}", t!("bins.mod.does-not-exist", name = name));
} }
self self
} }
@@ -68,6 +136,8 @@ impl MultiExecutable {
} }
pub fn execute(&self) { pub fn execute(&self) {
set_locale();
let mut popped = Vec::with_capacity(2); let mut popped = Vec::with_capacity(2);
let mut args = std::env::args_os().collect::<VecDeque<_>>(); let mut args = std::env::args_os().collect::<VecDeque<_>>();
@@ -96,11 +166,15 @@ impl MultiExecutable {
} }
let args = std::env::args().collect::<VecDeque<_>>(); let args = std::env::args().collect::<VecDeque<_>>();
eprintln!( eprintln!(
"unknown executable: {}", "{}",
args.get(1) t!(
.or_else(|| args.get(0)) "bins.mod.unknown-executable",
.map(|s| s.as_str()) name = args
.unwrap_or("N/A") .get(1)
.or_else(|| args.get(0))
.map(|s| s.as_str())
.unwrap_or("N/A")
)
); );
std::process::exit(1); std::process::exit(1);
} }

View File

@@ -3,6 +3,7 @@ use std::ffi::OsString;
use clap::Parser; use clap::Parser;
use futures::FutureExt; use futures::FutureExt;
use rpc_toolkit::CliApp; use rpc_toolkit::CliApp;
use rust_i18n::t;
use tokio::signal::unix::signal; use tokio::signal::unix::signal;
use tracing::instrument; use tracing::instrument;
@@ -77,7 +78,7 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.build() .build()
.expect("failed to initialize runtime"); .expect(&t!("bins.registry.failed-to-initialize-runtime"));
rt.block_on(inner_main(&config)) rt.block_on(inner_main(&config))
}; };
@@ -99,6 +100,7 @@ pub fn cli(args: impl IntoIterator<Item = OsString>) {
|cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?),
crate::registry::registry_api(), crate::registry::registry_api(),
) )
.mutate_command(super::translate_cli)
.run(args) .run(args)
{ {
match e.data { match e.data {

View File

@@ -19,6 +19,7 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
|cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?),
crate::main_api(), crate::main_api(),
) )
.mutate_command(super::translate_cli)
.run(args) .run(args)
{ {
match e.data { match e.data {

View File

@@ -25,7 +25,13 @@ async fn setup_or_init(
if let Some(firmware) = check_for_firmware_update() if let Some(firmware) = check_for_firmware_update()
.await .await
.map_err(|e| { .map_err(|e| {
tracing::warn!("Error checking for firmware update: {e}"); tracing::warn!(
"{}",
t!(
"bins.start-init.error-checking-firmware",
error = e.to_string()
)
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
}) })
.ok() .ok()
@@ -33,14 +39,21 @@ async fn setup_or_init(
{ {
let init_ctx = InitContext::init(config).await?; let init_ctx = InitContext::init(config).await?;
let handle = &init_ctx.progress; let handle = &init_ctx.progress;
let mut update_phase = handle.add_phase("Updating Firmware".into(), Some(10)); let mut update_phase =
let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1)); handle.add_phase(t!("bins.start-init.updating-firmware").into(), Some(10));
let mut reboot_phase = handle.add_phase(t!("bins.start-init.rebooting").into(), Some(1));
server.serve_ui_for(init_ctx); server.serve_ui_for(init_ctx);
update_phase.start(); update_phase.start();
if let Err(e) = update_firmware(firmware).await { if let Err(e) = update_firmware(firmware).await {
tracing::warn!("Error performing firmware update: {e}"); tracing::warn!(
"{}",
t!(
"bins.start-init.error-firmware-update",
error = e.to_string()
)
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} else { } else {
update_phase.complete(); update_phase.complete();
@@ -96,7 +109,13 @@ async fn setup_or_init(
.invoke(ErrorKind::NotFound) .invoke(ErrorKind::NotFound)
.await .await
{ {
tracing::error!("Failed to kill kiosk: {}", e); tracing::error!(
"{}",
t!(
"bins.start-init.failed-to-kill-kiosk",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
@@ -105,7 +124,7 @@ async fn setup_or_init(
Some(Err(e)) => return Err(e.clone_output()), Some(Err(e)) => return Err(e.clone_output()),
None => { None => {
return Err(Error::new( return Err(Error::new(
eyre!("Setup mode exited before setup completed"), eyre!("{}", t!("bins.start-init.setup-mode-exited")),
ErrorKind::Unknown, ErrorKind::Unknown,
)); ));
} }
@@ -115,7 +134,8 @@ async fn setup_or_init(
let handle = init_ctx.progress.clone(); let handle = init_ctx.progress.clone();
let err_channel = init_ctx.error.clone(); let err_channel = init_ctx.error.clone();
let mut disk_phase = handle.add_phase("Opening data drive".into(), Some(10)); let mut disk_phase =
handle.add_phase(t!("bins.start-init.opening-data-drive").into(), Some(10));
let init_phases = InitPhases::new(&handle); let init_phases = InitPhases::new(&handle);
let rpc_ctx_phases = InitRpcContextPhases::new(&handle); let rpc_ctx_phases = InitRpcContextPhases::new(&handle);
@@ -147,11 +167,12 @@ async fn setup_or_init(
.with_ctx(|_| (crate::ErrorKind::Filesystem, REPAIR_DISK_PATH))?; .with_ctx(|_| (crate::ErrorKind::Filesystem, REPAIR_DISK_PATH))?;
} }
disk_phase.complete(); disk_phase.complete();
tracing::info!("Loaded Disk"); tracing::info!("{}", t!("bins.start-init.loaded-disk"));
if requires_reboot.0 { if requires_reboot.0 {
tracing::info!("Rebooting..."); tracing::info!("{}", t!("bins.start-init.rebooting"));
let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1)); let mut reboot_phase =
handle.add_phase(t!("bins.start-init.rebooting").into(), Some(1));
reboot_phase.start(); reboot_phase.start();
return Ok(Err(Shutdown { return Ok(Err(Shutdown {
disk_guid: Some(disk_guid), disk_guid: Some(disk_guid),

View File

@@ -4,6 +4,7 @@ use std::time::Duration;
use clap::Parser; use clap::Parser;
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use rust_i18n::t;
use futures::{FutureExt, TryFutureExt}; use futures::{FutureExt, TryFutureExt};
use tokio::signal::unix::signal; use tokio::signal::unix::signal;
use tracing::instrument; use tracing::instrument;
@@ -112,11 +113,11 @@ async fn inner_main(
metrics_task metrics_task
.map_err(|e| { .map_err(|e| {
Error::new( Error::new(
eyre!("{}", e).wrap_err("Metrics daemon panicked!"), eyre!("{}", e).wrap_err(t!("bins.startd.metrics-daemon-panicked").to_string()),
ErrorKind::Unknown, ErrorKind::Unknown,
) )
}) })
.map_ok(|_| tracing::debug!("Metrics daemon Shutdown")) .map_ok(|_| tracing::debug!("{}", t!("bins.startd.metrics-daemon-shutdown")))
.await?; .await?;
let shutdown = shutdown_recv let shutdown = shutdown_recv
@@ -144,7 +145,7 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
.worker_threads(max(1, num_cpus::get())) .worker_threads(max(1, num_cpus::get()))
.enable_all() .enable_all()
.build() .build()
.expect("failed to initialize runtime"); .expect(&t!("bins.startd.failed-to-initialize-runtime"));
let res = rt.block_on(async { let res = rt.block_on(async {
let mut server = WebServer::new( let mut server = WebServer::new(
Acceptor::bind_upgradable(SelfContainedNetworkInterfaceListener::bind(BindTcp, 80)), Acceptor::bind_upgradable(SelfContainedNetworkInterfaceListener::bind(BindTcp, 80)),

View File

@@ -6,6 +6,7 @@ use std::time::Duration;
use clap::Parser; use clap::Parser;
use futures::FutureExt; use futures::FutureExt;
use rpc_toolkit::CliApp; use rpc_toolkit::CliApp;
use rust_i18n::t;
use tokio::signal::unix::signal; use tokio::signal::unix::signal;
use tracing::instrument; use tracing::instrument;
use visit_rs::Visit; use visit_rs::Visit;
@@ -70,7 +71,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
true true
} }
Err(e) => { Err(e) => {
tracing::error!("error adding ssl listener: {e}"); tracing::error!("{}", t!("bins.tunnel.error-adding-ssl-listener", error = e.to_string()));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
false false
@@ -92,7 +93,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
} }
.await .await
{ {
tracing::error!("error updating webserver bind: {e}"); tracing::error!("{}", t!("bins.tunnel.error-updating-webserver-bind", error = e.to_string()));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
tokio::time::sleep(Duration::from_secs(5)).await; tokio::time::sleep(Duration::from_secs(5)).await;
} }
@@ -157,7 +158,7 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.build() .build()
.expect("failed to initialize runtime"); .expect(&t!("bins.tunnel.failed-to-initialize-runtime"));
rt.block_on(inner_main(&config)) rt.block_on(inner_main(&config))
}; };
@@ -179,6 +180,7 @@ pub fn cli(args: impl IntoIterator<Item = OsString>) {
|cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?),
crate::tunnel::api::tunnel_api(), crate::tunnel::api::tunnel_api(),
) )
.mutate_command(super::translate_cli)
.run(args) .run(args)
{ {
match e.data { match e.data {

View File

@@ -166,14 +166,14 @@ impl CliContext {
.with_kind(crate::ErrorKind::Pem)?; .with_kind(crate::ErrorKind::Pem)?;
let secret = ed25519_dalek::SecretKey::try_from(&pair.secret_key[..]).map_err(|_| { let secret = ed25519_dalek::SecretKey::try_from(&pair.secret_key[..]).map_err(|_| {
Error::new( Error::new(
eyre!("pkcs8 key is of incorrect length"), eyre!("{}", t!("context.cli.pkcs8-key-incorrect-length")),
ErrorKind::OpenSsl, ErrorKind::OpenSsl,
) )
})?; })?;
return Ok(secret.into()) return Ok(secret.into())
} }
Err(Error::new( Err(Error::new(
eyre!("Developer Key does not exist! Please run `start-cli init-key` before running this command."), eyre!("{}", t!("context.cli.developer-key-does-not-exist")),
crate::ErrorKind::Uninitialized crate::ErrorKind::Uninitialized
)) ))
}) })
@@ -189,14 +189,14 @@ impl CliContext {
"http" => "ws", "http" => "ws",
_ => { _ => {
return Err(Error::new( return Err(Error::new(
eyre!("Cannot parse scheme from base URL"), eyre!("{}", t!("context.cli.cannot-parse-scheme-from-base-url")),
crate::ErrorKind::ParseUrl, crate::ErrorKind::ParseUrl,
) )
.into()); .into());
} }
}; };
url.set_scheme(ws_scheme) url.set_scheme(ws_scheme)
.map_err(|_| Error::new(eyre!("Cannot set URL scheme"), crate::ErrorKind::ParseUrl))?; .map_err(|_| Error::new(eyre!("{}", t!("context.cli.cannot-set-url-scheme")), crate::ErrorKind::ParseUrl))?;
url.path_segments_mut() url.path_segments_mut()
.map_err(|_| eyre!("Url cannot be base")) .map_err(|_| eyre!("Url cannot be base"))
.with_kind(crate::ErrorKind::ParseUrl)? .with_kind(crate::ErrorKind::ParseUrl)?

View File

@@ -27,7 +27,7 @@ impl DiagnosticContext {
disk_guid: Option<InternedString>, disk_guid: Option<InternedString>,
error: Error, error: Error,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
tracing::error!("Error: {}: Starting diagnostic UI", error); tracing::error!("{}", t!("context.diagnostic.starting-diagnostic-ui", error = error));
tracing::debug!("{:?}", error); tracing::debug!("{:?}", error);
let (shutdown, _) = tokio::sync::broadcast::channel(1); let (shutdown, _) = tokio::sync::broadcast::channel(1);

View File

@@ -84,7 +84,7 @@ pub struct RpcContextSeed {
} }
impl Drop for RpcContextSeed { impl Drop for RpcContextSeed {
fn drop(&mut self) { fn drop(&mut self) {
tracing::info!("RpcContext is dropped"); tracing::info!("{}", t!("context.rpc.rpc-context-dropped"));
} }
} }
@@ -155,7 +155,7 @@ impl RpcContext {
let peek = db.peek().await; let peek = db.peek().await;
let account = AccountInfo::load(&peek)?; let account = AccountInfo::load(&peek)?;
load_db.complete(); load_db.complete();
tracing::info!("Opened PatchDB"); tracing::info!("{}", t!("context.rpc.opened-patchdb"));
init_net_ctrl.start(); init_net_ctrl.start();
let (net_controller, os_net_service) = if let Some(InitResult { let (net_controller, os_net_service) = if let Some(InitResult {
@@ -172,15 +172,15 @@ impl RpcContext {
(net_ctrl, os_net_service) (net_ctrl, os_net_service)
}; };
init_net_ctrl.complete(); init_net_ctrl.complete();
tracing::info!("Initialized Net Controller"); tracing::info!("{}", t!("context.rpc.initialized-net-controller"));
if PLATFORM.ends_with("-nonfree") { if PLATFORM.ends_with("-nonfree") {
if let Err(e) = Command::new("nvidia-smi") if let Err(e) = Command::new("nvidia-smi")
.invoke(ErrorKind::ParseSysInfo) .invoke(ErrorKind::ParseSysInfo)
.await .await
{ {
tracing::warn!("nvidia-smi: {e}"); tracing::warn!("{}", t!("context.rpc.nvidia-smi-error", error = e));
tracing::info!("The above warning can be ignored if no NVIDIA card is present"); tracing::info!("{}", t!("context.rpc.nvidia-warning-can-be-ignored"));
} else { } else {
async { async {
let version: InternedString = String::from_utf8( let version: InternedString = String::from_utf8(
@@ -335,7 +335,7 @@ impl RpcContext {
is_closed: AtomicBool::new(false), is_closed: AtomicBool::new(false),
os_partitions: config.os_partitions.clone().ok_or_else(|| { os_partitions: config.os_partitions.clone().ok_or_else(|| {
Error::new( Error::new(
eyre!("OS Partition Information Missing"), eyre!("{}", t!("context.rpc.os-partition-info-missing")),
ErrorKind::Filesystem, ErrorKind::Filesystem,
) )
})?, })?,
@@ -365,9 +365,9 @@ impl RpcContext {
current_secret: Arc::new( current_secret: Arc::new(
Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).map_err(|e| { Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).map_err(|e| {
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
tracing::error!("Couldn't generate ec key"); tracing::error!("{}", t!("context.rpc.couldnt-generate-ec-key"));
Error::new( Error::new(
color_eyre::eyre::eyre!("Couldn't generate ec key"), color_eyre::eyre::eyre!("{}", t!("context.rpc.couldnt-generate-ec-key")),
crate::ErrorKind::Unknown, crate::ErrorKind::Unknown,
) )
})?, })?,
@@ -382,10 +382,10 @@ impl RpcContext {
let res = Self(seed.clone()); let res = Self(seed.clone());
res.cleanup_and_initialize(cleanup_init).await?; res.cleanup_and_initialize(cleanup_init).await?;
tracing::info!("Cleaned up transient states"); tracing::info!("{}", t!("context.rpc.cleaned-up-transient-states"));
crate::version::post_init(&res, run_migrations).await?; crate::version::post_init(&res, run_migrations).await?;
tracing::info!("Completed migrations"); tracing::info!("{}", t!("context.rpc.completed-migrations"));
Ok(res) Ok(res)
} }
@@ -394,7 +394,7 @@ impl RpcContext {
self.crons.mutate(|c| std::mem::take(c)); self.crons.mutate(|c| std::mem::take(c));
self.services.shutdown_all().await?; self.services.shutdown_all().await?;
self.is_closed.store(true, Ordering::SeqCst); self.is_closed.store(true, Ordering::SeqCst);
tracing::info!("RpcContext is shutdown"); tracing::info!("{}", t!("context.rpc.rpc-context-shutdown"));
Ok(()) Ok(())
} }
@@ -463,7 +463,7 @@ impl RpcContext {
.await .await
.result .result
{ {
tracing::error!("Error in session cleanup cron: {e}"); tracing::error!("{}", t!("context.rpc.error-in-session-cleanup-cron", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
} }

View File

@@ -33,7 +33,7 @@ use crate::util::sync::SyncMutex;
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref CURRENT_SECRET: Jwk = Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).unwrap_or_else(|e| { pub static ref CURRENT_SECRET: Jwk = Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).unwrap_or_else(|e| {
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
tracing::error!("Couldn't generate ec key"); tracing::error!("{}", t!("context.setup.couldnt-generate-ec-key"));
panic!("Couldn't generate ec key") panic!("Couldn't generate ec key")
}); });
} }
@@ -125,11 +125,11 @@ impl SetupContext {
.get_or_init(|| async { .get_or_init(|| async {
match f().await { match f().await {
Ok(res) => { Ok(res) => {
tracing::info!("Setup complete!"); tracing::info!("{}", t!("context.setup.setup-complete"));
Ok(res) Ok(res)
} }
Err(e) => { Err(e) => {
tracing::error!("Setup failed: {e}"); tracing::error!("{}", t!("context.setup.setup-failed", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
Err(e) Err(e)
} }
@@ -142,10 +142,10 @@ impl SetupContext {
) )
.map_err(|_| { .map_err(|_| {
if self.result.initialized() { if self.result.initialized() {
Error::new(eyre!("Setup already complete"), ErrorKind::InvalidRequest) Error::new(eyre!("{}", t!("context.setup.setup-already-complete")), ErrorKind::InvalidRequest)
} else { } else {
Error::new( Error::new(
eyre!("Setup already in progress"), eyre!("{}", t!("context.setup.setup-already-in-progress")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
) )
} }
@@ -195,7 +195,7 @@ impl SetupContext {
} }
.await .await
{ {
tracing::error!("Error in setup progress websocket: {e}"); tracing::error!("{}", t!("context.setup.error-in-setup-progress-websocket", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
}, },

View File

@@ -54,7 +54,7 @@ pub fn db<C: Context>() -> ParentHandler<C> {
"dump", "dump",
from_fn_async(cli_dump) from_fn_async(cli_dump)
.with_display_serializable() .with_display_serializable()
.with_about("Filter/query db to display tables and records"), .with_about("about.filter-query-db"),
) )
.subcommand("dump", from_fn_async(dump).no_cli()) .subcommand("dump", from_fn_async(dump).no_cli())
.subcommand( .subcommand(
@@ -65,13 +65,13 @@ pub fn db<C: Context>() -> ParentHandler<C> {
) )
.subcommand( .subcommand(
"put", "put",
put::<C>().with_about("Command for adding UI record to db"), put::<C>().with_about("about.command-add-ui-record-db"),
) )
.subcommand( .subcommand(
"apply", "apply",
from_fn_async(cli_apply) from_fn_async(cli_apply)
.no_display() .no_display()
.with_about("Update a db record"), .with_about("about.update-db-record"),
) )
.subcommand("apply", from_fn_async(apply).no_cli()) .subcommand("apply", from_fn_async(apply).no_cli())
} }
@@ -358,7 +358,7 @@ pub fn put<C: Context>() -> ParentHandler<C> {
"ui", "ui",
from_fn_async(ui) from_fn_async(ui)
.with_display_serializable() .with_display_serializable()
.with_about("Add path and value to db") .with_about("about.add-path-value-db")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -17,45 +17,46 @@ pub fn diagnostic<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"error", "error",
from_fn(error) from_fn(error)
.with_about("Display diagnostic error") .with_about("about.display-diagnostic-error")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"logs", "logs",
crate::system::logs::<DiagnosticContext>().with_about("Display OS logs"), crate::system::logs::<DiagnosticContext>().with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"logs", "logs",
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>) from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
.no_display() .no_display()
.with_about("Display OS logs"), .with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
crate::system::kernel_logs::<DiagnosticContext>().with_about("Display kernel logs"), crate::system::kernel_logs::<DiagnosticContext>()
.with_about("about.display-kernel-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>) from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
.no_display() .no_display()
.with_about("Display kernal logs"), .with_about("about.display-kernel-logs"),
) )
.subcommand( .subcommand(
"restart", "restart",
from_fn(restart) from_fn(restart)
.no_display() .no_display()
.with_about("Restart the server") .with_about("about.restart-server")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"disk", "disk",
disk::<C>().with_about("Command to remove disk from filesystem"), disk::<C>().with_about("about.command-remove-disk-filesystem"),
) )
.subcommand( .subcommand(
"rebuild", "rebuild",
from_fn_async(rebuild) from_fn_async(rebuild)
.no_display() .no_display()
.with_about("Teardown and rebuild service containers") .with_about("about.teardown-rebuild-containers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -89,7 +90,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
from_fn_async(forget_disk::<RpcContext>).no_display(), from_fn_async(forget_disk::<RpcContext>).no_display(),
) )
.no_display() .no_display()
.with_about("Remove disk from filesystem"), .with_about("about.remove-disk-filesystem"),
) )
.subcommand("repair", from_fn_async(|_: C| repair()).no_cli()) .subcommand("repair", from_fn_async(|_: C| repair()).no_cli())
.subcommand( .subcommand(
@@ -97,7 +98,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
CallRemoteHandler::<CliContext, _, _>::new( CallRemoteHandler::<CliContext, _, _>::new(
from_fn_async(|_: RpcContext| repair()) from_fn_async(|_: RpcContext| repair())
.no_display() .no_display()
.with_about("Repair disk in the event of corruption"), .with_about("about.repair-disk-corruption"),
), ),
) )
} }

View File

@@ -4,6 +4,7 @@ use std::path::Path;
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use futures::FutureExt; use futures::FutureExt;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use rust_i18n::t;
use tokio::process::Command; use tokio::process::Command;
use tracing::instrument; use tracing::instrument;
@@ -62,33 +63,31 @@ async fn e2fsck_runner(
let e2fsck_stderr = String::from_utf8(e2fsck_out.stderr)?; let e2fsck_stderr = String::from_utf8(e2fsck_out.stderr)?;
let code = e2fsck_out.status.code().ok_or_else(|| { let code = e2fsck_out.status.code().ok_or_else(|| {
Error::new( Error::new(
eyre!("e2fsck: process terminated by signal"), eyre!("{}", t!("disk.fsck.process-terminated-by-signal")),
crate::ErrorKind::DiskManagement, crate::ErrorKind::DiskManagement,
) )
})?; })?;
if code & 4 != 0 { if code & 4 != 0 {
tracing::error!( tracing::error!(
"some filesystem errors NOT corrected on {}:\n{}", "{}",
logicalname.as_ref().display(), t!("disk.fsck.errors-not-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr),
e2fsck_stderr,
); );
} else if code & 1 != 0 { } else if code & 1 != 0 {
tracing::warn!( tracing::warn!(
"filesystem errors corrected on {}:\n{}", "{}",
logicalname.as_ref().display(), t!("disk.fsck.errors-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr),
e2fsck_stderr,
); );
} }
if code < 8 { if code < 8 {
if code & 2 != 0 { if code & 2 != 0 {
tracing::warn!("reboot required"); tracing::warn!("{}", t!("disk.fsck.reboot-required"));
Ok(RequiresReboot(true)) Ok(RequiresReboot(true))
} else { } else {
Ok(RequiresReboot(false)) Ok(RequiresReboot(false))
} }
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("e2fsck: {}", e2fsck_stderr), eyre!("{}", t!("disk.fsck.e2fsck-error", stderr = e2fsck_stderr)),
crate::ErrorKind::DiskManagement, crate::ErrorKind::DiskManagement,
)) ))
} }

View File

@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use imbl_value::InternedString; use imbl_value::InternedString;
use rust_i18n::t;
use tokio::process::Command; use tokio::process::Command;
use tracing::instrument; use tracing::instrument;
@@ -225,7 +226,7 @@ pub async fn import<P: AsRef<Path>>(
.is_none() .is_none()
{ {
return Err(Error::new( return Err(Error::new(
eyre!("StartOS disk not found."), eyre!("{}", t!("disk.main.disk-not-found")),
crate::ErrorKind::DiskNotAvailable, crate::ErrorKind::DiskNotAvailable,
)); ));
} }
@@ -235,7 +236,7 @@ pub async fn import<P: AsRef<Path>>(
.any(|id| id == guid) .any(|id| id == guid)
{ {
return Err(Error::new( return Err(Error::new(
eyre!("A StartOS disk was found, but it is not the correct disk for this device."), eyre!("{}", t!("disk.main.incorrect-disk")),
crate::ErrorKind::IncorrectDisk, crate::ErrorKind::IncorrectDisk,
)); ));
} }

View File

@@ -51,7 +51,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
from_fn_async(list) from_fn_async(list)
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_disk_info(handle.params, result)) .with_custom_display_fn(|handle, result| display_disk_info(handle.params, result))
.with_about("List disk info") .with_about("about.list-disk-info")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand("repair", from_fn_async(|_: C| repair()).no_cli()) .subcommand("repair", from_fn_async(|_: C| repair()).no_cli())
@@ -60,7 +60,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
CallRemoteHandler::<CliContext, _, _>::new( CallRemoteHandler::<CliContext, _, _>::new(
from_fn_async(|_: RpcContext| repair()) from_fn_async(|_: RpcContext| repair())
.no_display() .no_display()
.with_about("Repair disk in the event of corruption"), .with_about("about.repair-disk-corruption"),
), ),
) )
} }

View File

@@ -23,9 +23,8 @@ pub async fn bind<P0: AsRef<Path>, P1: AsRef<Path>>(
read_only: bool, read_only: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
tracing::info!( tracing::info!(
"Binding {} to {}", "{}",
src.as_ref().display(), t!("disk.mount.binding", src = src.as_ref().display(), dst = dst.as_ref().display())
dst.as_ref().display()
); );
if is_mountpoint(&dst).await? { if is_mountpoint(&dst).await? {
unmount(dst.as_ref(), true).await?; unmount(dst.as_ref(), true).await?;

View File

@@ -95,7 +95,7 @@ pub async fn get_vendor<P: AsRef<Path>>(path: P) -> Result<Option<String>, Error
Path::new(SYS_BLOCK_PATH) Path::new(SYS_BLOCK_PATH)
.join(path.as_ref().strip_prefix("/dev").map_err(|_| { .join(path.as_ref().strip_prefix("/dev").map_err(|_| {
Error::new( Error::new(
eyre!("not a canonical block device"), eyre!("{}", t!("disk.util.not-canonical-block-device")),
crate::ErrorKind::BlockDevice, crate::ErrorKind::BlockDevice,
) )
})?) })?)
@@ -118,7 +118,7 @@ pub async fn get_model<P: AsRef<Path>>(path: P) -> Result<Option<String>, Error>
Path::new(SYS_BLOCK_PATH) Path::new(SYS_BLOCK_PATH)
.join(path.as_ref().strip_prefix("/dev").map_err(|_| { .join(path.as_ref().strip_prefix("/dev").map_err(|_| {
Error::new( Error::new(
eyre!("not a canonical block device"), eyre!("{}", t!("disk.util.not-canonical-block-device")),
crate::ErrorKind::BlockDevice, crate::ErrorKind::BlockDevice,
) )
})?) })?)
@@ -374,23 +374,22 @@ async fn disk_info(disk: PathBuf) -> DiskInfo {
.await .await
.map_err(|e| { .map_err(|e| {
tracing::warn!( tracing::warn!(
"Could not get partition table of {}: {}", "{}",
disk.display(), t!("disk.util.could-not-get-partition-table", disk = disk.display(), error = e.source)
e.source
) )
}) })
.unwrap_or_default(); .unwrap_or_default();
let vendor = get_vendor(&disk) let vendor = get_vendor(&disk)
.await .await
.map_err(|e| tracing::warn!("Could not get vendor of {}: {}", disk.display(), e.source)) .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-vendor", disk = disk.display(), error = e.source)))
.unwrap_or_default(); .unwrap_or_default();
let model = get_model(&disk) let model = get_model(&disk)
.await .await
.map_err(|e| tracing::warn!("Could not get model of {}: {}", disk.display(), e.source)) .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-model", disk = disk.display(), error = e.source)))
.unwrap_or_default(); .unwrap_or_default();
let capacity = get_capacity(&disk) let capacity = get_capacity(&disk)
.await .await
.map_err(|e| tracing::warn!("Could not get capacity of {}: {}", disk.display(), e.source)) .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-capacity", disk = disk.display(), error = e.source)))
.unwrap_or_default(); .unwrap_or_default();
DiskInfo { DiskInfo {
logicalname: disk, logicalname: disk,
@@ -407,21 +406,21 @@ async fn part_info(part: PathBuf) -> PartitionInfo {
let mut start_os = BTreeMap::new(); let mut start_os = BTreeMap::new();
let label = get_label(&part) let label = get_label(&part)
.await .await
.map_err(|e| tracing::warn!("Could not get label of {}: {}", part.display(), e.source)) .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-label", part = part.display(), error = e.source)))
.unwrap_or_default(); .unwrap_or_default();
let capacity = get_capacity(&part) let capacity = get_capacity(&part)
.await .await
.map_err(|e| tracing::warn!("Could not get capacity of {}: {}", part.display(), e.source)) .map_err(|e| tracing::warn!("{}", t!("disk.util.could-not-get-capacity-part", part = part.display(), error = e.source)))
.unwrap_or_default(); .unwrap_or_default();
let mut used = None; let mut used = None;
match TmpMountGuard::mount(&BlockDev::new(&part), ReadOnly).await { match TmpMountGuard::mount(&BlockDev::new(&part), ReadOnly).await {
Err(e) => tracing::warn!("Could not collect usage information: {}", e.source), Err(e) => tracing::warn!("{}", t!("disk.util.could-not-collect-usage-info", error = e.source)),
Ok(mount_guard) => { Ok(mount_guard) => {
used = get_used(mount_guard.path()) used = get_used(mount_guard.path())
.await .await
.map_err(|e| { .map_err(|e| {
tracing::warn!("Could not get usage of {}: {}", part.display(), e.source) tracing::warn!("{}", t!("disk.util.could-not-get-usage", part = part.display(), error = e.source))
}) })
.ok(); .ok();
match recovery_info(mount_guard.path()).await { match recovery_info(mount_guard.path()).await {
@@ -429,11 +428,11 @@ async fn part_info(part: PathBuf) -> PartitionInfo {
start_os = a; start_os = a;
} }
Err(e) => { Err(e) => {
tracing::error!("Error fetching unencrypted backup metadata: {}", e); tracing::error!("{}", t!("disk.util.error-fetching-backup-metadata", error = e));
} }
} }
if let Err(e) = mount_guard.unmount().await { if let Err(e) = mount_guard.unmount().await {
tracing::error!("Error unmounting partition {}: {}", part.display(), e); tracing::error!("{}", t!("disk.util.error-unmounting-partition", part = part.display(), error = e));
} }
} }
} }
@@ -474,7 +473,7 @@ fn parse_pvscan_output(pvscan_output: &str) -> BTreeMap<PathBuf, Option<Interned
ret.insert(PathBuf::from(pv), vg.map(InternedString::intern)); ret.insert(PathBuf::from(pv), vg.map(InternedString::intern));
} }
Err(_) => { Err(_) => {
tracing::warn!("Failed to parse pvscan output line: {}", entry); tracing::warn!("{}", t!("disk.util.failed-to-parse-pvscan", line = entry));
} }
} }
} }

View File

@@ -9,6 +9,7 @@ use rpc_toolkit::reqwest;
use rpc_toolkit::yajrc::{ use rpc_toolkit::yajrc::{
INVALID_PARAMS_ERROR, INVALID_REQUEST_ERROR, METHOD_NOT_FOUND_ERROR, PARSE_ERROR, RpcError, INVALID_PARAMS_ERROR, INVALID_REQUEST_ERROR, METHOD_NOT_FOUND_ERROR, PARSE_ERROR, RpcError,
}; };
use rust_i18n::t;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tokio_rustls::rustls; use tokio_rustls::rustls;
@@ -100,94 +101,94 @@ pub enum ErrorKind {
SetSysInfo = 79, SetSysInfo = 79,
} }
impl ErrorKind { impl ErrorKind {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> String {
use ErrorKind::*; use ErrorKind::*;
match self { match self {
Unknown => "Unknown Error", Unknown => t!("error.unknown"),
Filesystem => "Filesystem I/O Error", Filesystem => t!("error.filesystem"),
Docker => "Docker Error", Docker => t!("error.docker"),
ConfigSpecViolation => "Config Spec Violation", ConfigSpecViolation => t!("error.config-spec-violation"),
ConfigRulesViolation => "Config Rules Violation", ConfigRulesViolation => t!("error.config-rules-violation"),
NotFound => "Not Found", NotFound => t!("error.not-found"),
IncorrectPassword => "Incorrect Password", IncorrectPassword => t!("error.incorrect-password"),
VersionIncompatible => "Version Incompatible", VersionIncompatible => t!("error.version-incompatible"),
Network => "Network Error", Network => t!("error.network"),
Registry => "Registry Error", Registry => t!("error.registry"),
Serialization => "Serialization Error", Serialization => t!("error.serialization"),
Deserialization => "Deserialization Error", Deserialization => t!("error.deserialization"),
Utf8 => "UTF-8 Parse Error", Utf8 => t!("error.utf8"),
ParseVersion => "Version Parsing Error", ParseVersion => t!("error.parse-version"),
IncorrectDisk => "Incorrect Disk", IncorrectDisk => t!("error.incorrect-disk"),
// Nginx => "Nginx Error", // Nginx => t!("error.nginx"),
Dependency => "Dependency Error", Dependency => t!("error.dependency"),
ParseS9pk => "S9PK Parsing Error", ParseS9pk => t!("error.parse-s9pk"),
ParseUrl => "URL Parsing Error", ParseUrl => t!("error.parse-url"),
DiskNotAvailable => "Disk Not Available", DiskNotAvailable => t!("error.disk-not-available"),
BlockDevice => "Block Device Error", BlockDevice => t!("error.block-device"),
InvalidOnionAddress => "Invalid Onion Address", InvalidOnionAddress => t!("error.invalid-onion-address"),
Pack => "Pack Error", Pack => t!("error.pack"),
ValidateS9pk => "S9PK Validation Error", ValidateS9pk => t!("error.validate-s9pk"),
DiskCorrupted => "Disk Corrupted", // Remove DiskCorrupted => t!("error.disk-corrupted"), // Remove
Tor => "Tor Daemon Error", Tor => t!("error.tor"),
ConfigGen => "Config Generation Error", ConfigGen => t!("error.config-gen"),
ParseNumber => "Number Parsing Error", ParseNumber => t!("error.parse-number"),
Database => "Database Error", Database => t!("error.database"),
InvalidId => "Invalid ID", InvalidId => t!("error.invalid-id"),
InvalidSignature => "Invalid Signature", InvalidSignature => t!("error.invalid-signature"),
Backup => "Backup Error", Backup => t!("error.backup"),
Restore => "Restore Error", Restore => t!("error.restore"),
Authorization => "Unauthorized", Authorization => t!("error.authorization"),
AutoConfigure => "Auto-Configure Error", AutoConfigure => t!("error.auto-configure"),
Action => "Action Failed", Action => t!("error.action"),
RateLimited => "Rate Limited", RateLimited => t!("error.rate-limited"),
InvalidRequest => "Invalid Request", InvalidRequest => t!("error.invalid-request"),
MigrationFailed => "Migration Failed", MigrationFailed => t!("error.migration-failed"),
Uninitialized => "Uninitialized", Uninitialized => t!("error.uninitialized"),
ParseNetAddress => "Net Address Parsing Error", ParseNetAddress => t!("error.parse-net-address"),
ParseSshKey => "SSH Key Parsing Error", ParseSshKey => t!("error.parse-ssh-key"),
SoundError => "Sound Interface Error", SoundError => t!("error.sound-error"),
ParseTimestamp => "Timestamp Parsing Error", ParseTimestamp => t!("error.parse-timestamp"),
ParseSysInfo => "System Info Parsing Error", ParseSysInfo => t!("error.parse-sys-info"),
Wifi => "WiFi Internal Error", Wifi => t!("error.wifi"),
Journald => "Journald Error", Journald => t!("error.journald"),
DiskManagement => "Disk Management Error", DiskManagement => t!("error.disk-management"),
OpenSsl => "OpenSSL Internal Error", OpenSsl => t!("error.openssl"),
PasswordHashGeneration => "Password Hash Generation Error", PasswordHashGeneration => t!("error.password-hash-generation"),
DiagnosticMode => "Server is in Diagnostic Mode", DiagnosticMode => t!("error.diagnostic-mode"),
ParseDbField => "Database Field Parse Error", ParseDbField => t!("error.parse-db-field"),
Duplicate => "Duplication Error", Duplicate => t!("error.duplicate"),
MultipleErrors => "Multiple Errors", MultipleErrors => t!("error.multiple-errors"),
Incoherent => "Incoherent", Incoherent => t!("error.incoherent"),
InvalidBackupTargetId => "Invalid Backup Target ID", InvalidBackupTargetId => t!("error.invalid-backup-target-id"),
ProductKeyMismatch => "Incompatible Product Keys", ProductKeyMismatch => t!("error.product-key-mismatch"),
LanPortConflict => "Incompatible LAN Port Configuration", LanPortConflict => t!("error.lan-port-conflict"),
Javascript => "Javascript Engine Error", Javascript => t!("error.javascript"),
Pem => "PEM Encoding Error", Pem => t!("error.pem"),
TLSInit => "TLS Backend Initialization Error", TLSInit => t!("error.tls-init"),
Ascii => "ASCII Parse Error", Ascii => t!("error.ascii"),
MissingHeader => "Missing Header", MissingHeader => t!("error.missing-header"),
Grub => "Grub Error", Grub => t!("error.grub"),
Systemd => "Systemd Error", Systemd => t!("error.systemd"),
OpenSsh => "OpenSSH Error", OpenSsh => t!("error.openssh"),
Zram => "Zram Error", Zram => t!("error.zram"),
Lshw => "LSHW Error", Lshw => t!("error.lshw"),
CpuSettings => "CPU Settings Error", CpuSettings => t!("error.cpu-settings"),
Firmware => "Firmware Error", Firmware => t!("error.firmware"),
Timeout => "Timeout Error", Timeout => t!("error.timeout"),
Lxc => "LXC Error", Lxc => t!("error.lxc"),
Cancelled => "Cancelled", Cancelled => t!("error.cancelled"),
Git => "Git Error", Git => t!("error.git"),
DBus => "DBus Error", DBus => t!("error.dbus"),
InstallFailed => "Install Failed", InstallFailed => t!("error.install-failed"),
UpdateFailed => "Update Failed", UpdateFailed => t!("error.update-failed"),
Smtp => "SMTP Error", Smtp => t!("error.smtp"),
SetSysInfo => "Error Setting System Info", SetSysInfo => t!("error.set-sys-info"),
} }.to_string()
} }
} }
impl Display for ErrorKind { impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str()) write!(f, "{}", &self.as_str())
} }
} }
@@ -201,7 +202,7 @@ pub struct Error {
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {:#}", self.kind.as_str(), self.source) write!(f, "{}: {:#}", &self.kind.as_str(), self.source)
} }
} }
impl Debug for Error { impl Debug for Error {
@@ -209,7 +210,7 @@ impl Debug for Error {
write!( write!(
f, f,
"{}: {:?}", "{}: {:?}",
self.kind.as_str(), &self.kind.as_str(),
self.debug.as_ref().unwrap_or(&self.source) self.debug.as_ref().unwrap_or(&self.source)
) )
} }

View File

@@ -81,26 +81,28 @@ impl InitPhases {
pub fn new(handle: &FullProgressTracker) -> Self { pub fn new(handle: &FullProgressTracker) -> Self {
Self { Self {
preinit: if Path::new("/media/startos/config/preinit.sh").exists() { preinit: if Path::new("/media/startos/config/preinit.sh").exists() {
Some(handle.add_phase("Running preinit.sh".into(), Some(5))) Some(handle.add_phase(t!("init.running-preinit").into(), Some(5)))
} else { } else {
None None
}, },
local_auth: handle.add_phase("Enabling local authentication".into(), Some(1)), local_auth: handle.add_phase(t!("init.enabling-local-auth").into(), Some(1)),
load_database: handle.add_phase("Loading database".into(), Some(5)), load_database: handle.add_phase(t!("init.loading-database").into(), Some(5)),
load_ssh_keys: handle.add_phase("Loading SSH Keys".into(), Some(1)), load_ssh_keys: handle.add_phase(t!("init.loading-ssh-keys").into(), Some(1)),
start_net: handle.add_phase("Starting network controller".into(), Some(1)), start_net: handle.add_phase(t!("init.starting-network-controller").into(), Some(1)),
mount_logs: handle.add_phase("Switching logs to write to data drive".into(), Some(1)), mount_logs: handle.add_phase(t!("init.switching-logs-to-data-drive").into(), Some(1)),
load_ca_cert: handle.add_phase("Loading CA certificate".into(), Some(1)), load_ca_cert: handle.add_phase(t!("init.loading-ca-certificate").into(), Some(1)),
load_wifi: handle.add_phase("Loading WiFi configuration".into(), Some(1)), load_wifi: handle.add_phase(t!("init.loading-wifi-configuration").into(), Some(1)),
init_tmp: handle.add_phase("Initializing temporary files".into(), Some(1)), init_tmp: handle.add_phase(t!("init.initializing-temporary-files").into(), Some(1)),
set_governor: handle.add_phase("Setting CPU performance profile".into(), Some(1)), set_governor: handle
sync_clock: handle.add_phase("Synchronizing system clock".into(), Some(10)), .add_phase(t!("init.setting-cpu-performance-profile").into(), Some(1)),
enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)), sync_clock: handle.add_phase(t!("init.synchronizing-system-clock").into(), Some(10)),
update_server_info: handle.add_phase("Updating server info".into(), Some(1)), enable_zram: handle.add_phase(t!("init.enabling-zram").into(), Some(1)),
launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)), update_server_info: handle.add_phase(t!("init.updating-server-info").into(), Some(1)),
validate_db: handle.add_phase("Validating database".into(), Some(1)), launch_service_network: handle
.add_phase(t!("init.launching-service-intranet").into(), Some(1)),
validate_db: handle.add_phase(t!("init.validating-database").into(), Some(1)),
postinit: if Path::new("/media/startos/config/postinit.sh").exists() { postinit: if Path::new("/media/startos/config/postinit.sh").exists() {
Some(handle.add_phase("Running postinit.sh".into(), Some(5))) Some(handle.add_phase(t!("init.running-postinit").into(), Some(5)))
} else { } else {
None None
}, },
@@ -127,7 +129,14 @@ pub async fn run_script<P: AsRef<Path>>(path: P, mut progress: PhaseProgressTrac
} }
.await .await
{ {
tracing::error!("Error Running {}: {}", script.display(), e); tracing::error!(
"{}",
t!(
"init.error-running-script",
script = script.display(),
error = e
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
progress.complete(); progress.complete();
@@ -230,6 +239,7 @@ pub async fn init(
.arg("-R") .arg("-R")
.arg("+C") .arg("+C")
.arg("/var/log/journal") .arg("/var/log/journal")
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::Filesystem) .invoke(ErrorKind::Filesystem)
.await .await
{ {
@@ -314,14 +324,17 @@ pub async fn init(
{ {
Some(governor) Some(governor)
} else { } else {
tracing::warn!("CPU Governor \"{governor}\" Not Available"); tracing::warn!(
"{}",
t!("init.cpu-governor-not-available", governor = governor)
);
None None
} }
} else { } else {
cpupower::get_preferred_governor().await? cpupower::get_preferred_governor().await?
}; };
if let Some(governor) = governor { if let Some(governor) = governor {
tracing::info!("Setting CPU Governor to \"{governor}\""); tracing::info!("{}", t!("init.setting-cpu-governor", governor = governor));
cpupower::set_governor(governor).await?; cpupower::set_governor(governor).await?;
} }
set_governor.complete(); set_governor.complete();
@@ -349,14 +362,14 @@ pub async fn init(
} }
} }
if !ntp_synced { if !ntp_synced {
tracing::warn!("Timed out waiting for system time to synchronize"); tracing::warn!("{}", t!("init.clock-sync-timeout"));
} }
sync_clock.complete(); sync_clock.complete();
enable_zram.start(); enable_zram.start();
if server_info.as_zram().de()? { if server_info.as_zram().de()? {
crate::system::enable_zram().await?; crate::system::enable_zram().await?;
tracing::info!("Enabled ZRAM"); tracing::info!("{}", t!("init.enabled-zram"));
} }
enable_zram.complete(); enable_zram.complete();
@@ -404,7 +417,7 @@ pub async fn init(
run_script("/media/startos/config/postinit.sh", progress).await; run_script("/media/startos/config/postinit.sh", progress).await;
} }
tracing::info!("System initialized."); tracing::info!("{}", t!("init.system-initialized"));
Ok(InitResult { Ok(InitResult {
net_ctrl, net_ctrl,
@@ -416,30 +429,30 @@ pub fn init_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
.subcommand( .subcommand(
"logs", "logs",
crate::system::logs::<InitContext>().with_about("Disply OS logs"), crate::system::logs::<InitContext>().with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"logs", "logs",
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>) from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
.no_display() .no_display()
.with_about("Display OS logs"), .with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
crate::system::kernel_logs::<InitContext>().with_about("Display kernel logs"), crate::system::kernel_logs::<InitContext>().with_about("about.display-kernel-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>) from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
.no_display() .no_display()
.with_about("Display kernel logs"), .with_about("about.display-kernel-logs"),
) )
.subcommand("subscribe", from_fn_async(init_progress).no_cli()) .subcommand("subscribe", from_fn_async(init_progress).no_cli())
.subcommand( .subcommand(
"subscribe", "subscribe",
from_fn_async(cli_init_progress) from_fn_async(cli_init_progress)
.no_display() .no_display()
.with_about("Get initialization progress"), .with_about("about.get-initialization-progress"),
) )
} }
@@ -495,7 +508,7 @@ pub async fn init_progress(ctx: InitContext) -> Result<InitProgressRes, Error> {
); );
if let Err(e) = ws.close_result(res.map(|_| "complete")).await { if let Err(e) = ws.close_result(res.map(|_| "complete")).await {
tracing::error!("error closing init progress websocket: {e}"); tracing::error!("{}", t!("init.error-closing-websocket", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
}, },
@@ -526,7 +539,7 @@ pub async fn cli_init_progress(
.await?, .await?,
)?; )?;
let mut ws = ctx.ws_continuation(res.guid).await?; let mut ws = ctx.ws_continuation(res.guid).await?;
let mut bar = PhasedProgressBar::new("Initializing..."); let mut bar = PhasedProgressBar::new(&t!("init.initializing"));
while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? { while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? {
if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg { if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg {
bar.update(&serde_json::from_str(&msg).with_kind(ErrorKind::Deserialization)?); bar.update(&serde_json::from_str(&msg).with_kind(ErrorKind::Deserialization)?);

View File

@@ -135,80 +135,63 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
let mut api = ParentHandler::new() let mut api = ParentHandler::new()
.subcommand( .subcommand(
"git-info", "git-info",
from_fn(|_: C| version::git_info()).with_about("Display the githash of StartOS CLI"), from_fn(|_: C| version::git_info()).with_about("about.display-githash"),
) )
.subcommand( .subcommand(
"echo", "echo",
from_fn(echo::<RpcContext>) from_fn(echo::<RpcContext>)
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_about("Echo a message") .with_about("about.echo-message")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"state", "state",
from_fn(|_: RpcContext| Ok::<_, Error>(ApiState::Running)) from_fn(|_: RpcContext| Ok::<_, Error>(ApiState::Running))
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_about("Display the API that is currently serving") .with_about("about.display-current-api")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"state", "state",
from_fn(|_: InitContext| Ok::<_, Error>(ApiState::Initializing)) from_fn(|_: InitContext| Ok::<_, Error>(ApiState::Initializing))
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_about("Display the API that is currently serving") .with_about("about.display-current-api")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"state", "state",
from_fn(|_: DiagnosticContext| Ok::<_, Error>(ApiState::Error)) from_fn(|_: DiagnosticContext| Ok::<_, Error>(ApiState::Error))
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_about("Display the API that is currently serving") .with_about("about.display-current-api")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand("server", server::<C>().with_about("about.commands-server"))
"server",
server::<C>()
.with_about("Commands related to the server i.e. restart, update, and shutdown"),
)
.subcommand( .subcommand(
"package", "package",
package::<C>().with_about("Commands related to packages"), package::<C>().with_about("about.commands-packages"),
) )
.subcommand( .subcommand(
"net", "net",
net::net_api::<C>().with_about("Network commands related to tor and dhcp"), net::net_api::<C>().with_about("about.network-commands"),
) )
.subcommand( .subcommand(
"auth", "auth",
auth::auth::<C, RpcContext>() auth::auth::<C, RpcContext>().with_about("about.commands-authentication"),
.with_about("Commands related to Authentication i.e. login, logout"),
)
.subcommand(
"db",
db::db::<C>().with_about("Commands to interact with the db i.e. dump, put, apply"),
)
.subcommand(
"ssh",
ssh::ssh::<C>()
.with_about("Commands for interacting with ssh keys i.e. add, delete, list"),
) )
.subcommand("db", db::db::<C>().with_about("about.commands-db"))
.subcommand("ssh", ssh::ssh::<C>().with_about("about.commands-ssh-keys"))
.subcommand( .subcommand(
"wifi", "wifi",
net::wifi::wifi::<C>() net::wifi::wifi::<C>().with_about("about.commands-wifi"),
.with_about("Commands related to wifi networks i.e. add, connect, delete"),
)
.subcommand(
"disk",
disk::disk::<C>().with_about("Commands for listing disk info and repairing"),
) )
.subcommand("disk", disk::disk::<C>().with_about("about.commands-disk"))
.subcommand( .subcommand(
"notification", "notification",
notifications::notification::<C>().with_about("Create, delete, or list notifications"), notifications::notification::<C>().with_about("about.commands-notifications"),
) )
.subcommand( .subcommand(
"backup", "backup",
backup::backup::<C>() backup::backup::<C>().with_about("about.commands-backup"),
.with_about("Commands related to backup creation and backup targets"),
) )
.subcommand( .subcommand(
"registry", "registry",
@@ -219,7 +202,7 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
) )
.subcommand( .subcommand(
"registry", "registry",
registry::registry_api::<CliContext>().with_about("Commands related to the registry"), registry::registry_api::<CliContext>().with_about("about.commands-registry"),
) )
.subcommand( .subcommand(
"tunnel", "tunnel",
@@ -228,31 +211,26 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
) )
.subcommand( .subcommand(
"tunnel", "tunnel",
tunnel::api::tunnel_api::<CliContext>().with_about("Commands related to StartTunnel"), tunnel::api::tunnel_api::<CliContext>().with_about("about.commands-tunnel"),
)
.subcommand(
"s9pk",
s9pk::rpc::s9pk().with_about("Commands for interacting with s9pk files"),
) )
.subcommand("s9pk", s9pk::rpc::s9pk().with_about("about.commands-s9pk"))
.subcommand( .subcommand(
"util", "util",
util::rpc::util::<C>().with_about("Command for calculating the blake3 hash of a file"), util::rpc::util::<C>().with_about("about.command-blake3-hash"),
) )
.subcommand( .subcommand(
"init-key", "init-key",
from_fn_async(developer::init) from_fn_async(developer::init)
.no_display() .no_display()
.with_about("Create developer key if it doesn't exist"), .with_about("about.create-developer-key"),
) )
.subcommand( .subcommand(
"pubkey", "pubkey",
from_fn_blocking(developer::pubkey) from_fn_blocking(developer::pubkey).with_about("about.get-developer-pubkey"),
.with_about("Get public key for developer private key"),
) )
.subcommand( .subcommand(
"diagnostic", "diagnostic",
diagnostic::diagnostic::<C>() diagnostic::diagnostic::<C>().with_about("about.commands-diagnostic"),
.with_about("Commands to display logs, restart the server, etc"),
) )
.subcommand("init", init::init_api::<C>()) .subcommand("init", init::init_api::<C>())
.subcommand("setup", setup::setup::<C>()); .subcommand("setup", setup::setup::<C>());
@@ -265,7 +243,7 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
"flash-os", "flash-os",
from_fn_async(os_install::cli_install_os) from_fn_async(os_install::cli_install_os)
.no_display() .no_display()
.with_about("Flash StartOS to a disk from a squashfs"), .with_about("about.flash-startos"),
); );
} }
api api
@@ -280,29 +258,32 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
system::display_time(handle.params, result) system::display_time(handle.params, result)
}) })
.with_about("Display current time and server uptime") .with_about("about.display-time-uptime")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"experimental", "experimental",
system::experimental::<C>() system::experimental::<C>().with_about("about.commands-experimental"),
.with_about("Commands related to configuring experimental options such as zram and cpu governor"),
) )
.subcommand( .subcommand(
"logs", "logs",
system::logs::<RpcContext>().with_about("Display OS logs"), system::logs::<RpcContext>().with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"logs", "logs",
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().with_about("Display OS logs"), from_fn_async(logs::cli_logs::<RpcContext, Empty>)
.no_display()
.with_about("about.display-os-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
system::kernel_logs::<RpcContext>().with_about("Display Kernel logs"), system::kernel_logs::<RpcContext>().with_about("about.display-kernel-logs"),
) )
.subcommand( .subcommand(
"kernel-logs", "kernel-logs",
from_fn_async(logs::cli_logs::<RpcContext, Empty>).no_display().with_about("Display Kernel logs"), from_fn_async(logs::cli_logs::<RpcContext, Empty>)
.no_display()
.with_about("about.display-kernel-logs"),
) )
.subcommand( .subcommand(
"metrics", "metrics",
@@ -310,35 +291,31 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.root_handler( .root_handler(
from_fn_async(system::metrics) from_fn_async(system::metrics)
.with_display_serializable() .with_display_serializable()
.with_about("Display information about the server i.e. temperature, RAM, CPU, and disk usage") .with_about("about.display-server-metrics")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
)
.subcommand(
"follow",
from_fn_async(system::metrics_follow)
.no_cli()
) )
.subcommand("follow", from_fn_async(system::metrics_follow).no_cli()),
) )
.subcommand( .subcommand(
"shutdown", "shutdown",
from_fn_async(shutdown::shutdown) from_fn_async(shutdown::shutdown)
.no_display() .no_display()
.with_about("Shutdown the server") .with_about("about.shutdown-server")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"restart", "restart",
from_fn_async(shutdown::restart) from_fn_async(shutdown::restart)
.no_display() .no_display()
.with_about("Restart the server") .with_about("about.restart-server")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"rebuild", "rebuild",
from_fn_async(shutdown::rebuild) from_fn_async(shutdown::rebuild)
.no_display() .no_display()
.with_about("Teardown and rebuild service containers") .with_about("about.teardown-rebuild-containers")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"update", "update",
@@ -348,7 +325,9 @@ pub fn server<C: Context>() -> ParentHandler<C> {
) )
.subcommand( .subcommand(
"update", "update",
from_fn_async(update::cli_update_system).no_display().with_about("Check a given registry for StartOS updates and update if available"), from_fn_async(update::cli_update_system)
.no_display()
.with_about("about.check-update-startos"),
) )
.subcommand( .subcommand(
"update-firmware", "update-firmware",
@@ -363,37 +342,41 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|_handle, result| { .with_custom_display_fn(|_handle, result| {
Ok(firmware::display_firmware_update_result(result)) Ok(firmware::display_firmware_update_result(result))
}) })
.with_about("Update the mainboard's firmware to the latest firmware available in this version of StartOS if available. Note: This command does not reach out to the Internet") .with_about("about.update-firmware")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"set-smtp", "set-smtp",
from_fn_async(system::set_system_smtp) from_fn_async(system::set_system_smtp)
.no_display() .no_display()
.with_about("Set system smtp server and credentials") .with_about("about.set-smtp")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"test-smtp", "test-smtp",
from_fn_async(system::test_smtp) from_fn_async(system::test_smtp)
.no_display() .no_display()
.with_about("Send test email using provided smtp server and credentials") .with_about("about.test-smtp")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"clear-smtp", "clear-smtp",
from_fn_async(system::clear_system_smtp) from_fn_async(system::clear_system_smtp)
.no_display() .no_display()
.with_about("Remove system smtp server and credentials") .with_about("about.clear-smtp")
.with_call_remote::<CliContext>() .with_call_remote::<CliContext>(),
).subcommand("host", net::host::server_host_api::<C>().with_about("Commands for modifying the host for the system ui")) )
.subcommand(
"host",
net::host::server_host_api::<C>().with_about("about.commands-host-system-ui"),
)
} }
pub fn package<C: Context>() -> ParentHandler<C> { pub fn package<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
.subcommand( .subcommand(
"action", "action",
action::action_api::<C>().with_about("Commands to get action input or run an action"), action::action_api::<C>().with_about("about.commands-action"),
) )
.subcommand( .subcommand(
"install", "install",
@@ -411,13 +394,13 @@ pub fn package<C: Context>() -> ParentHandler<C> {
"install", "install",
from_fn_async_local(install::cli_install) from_fn_async_local(install::cli_install)
.no_display() .no_display()
.with_about("Install a package from a marketplace or via sideloading"), .with_about("about.install-package"),
) )
.subcommand( .subcommand(
"cancel-install", "cancel-install",
from_fn(install::cancel_install) from_fn(install::cancel_install)
.no_display() .no_display()
.with_about("Cancel an install of a package") .with_about("about.cancel-install-package")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -425,21 +408,21 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(install::uninstall) from_fn_async(install::uninstall)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a package") .with_about("about.remove-package")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"list", "list",
from_fn_async(install::list) from_fn_async(install::list)
.with_display_serializable() .with_display_serializable()
.with_about("List installed packages") .with_about("about.list-installed-packages")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"installed-version", "installed-version",
from_fn_async(install::installed_version) from_fn_async(install::installed_version)
.with_display_serializable() .with_display_serializable()
.with_about("Display installed version for a PackageId") .with_about("about.display-installed-version")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -447,7 +430,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::start) from_fn_async(control::start)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Start a service") .with_about("about.start-service")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -455,7 +438,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::stop) from_fn_async(control::stop)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Stop a service") .with_about("about.stop-service")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -463,7 +446,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::restart) from_fn_async(control::restart)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Restart a service") .with_about("about.restart-service")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -471,7 +454,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(service::rebuild) from_fn_async(service::rebuild)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Rebuild service container") .with_about("about.rebuild-service-container")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -511,35 +494,34 @@ pub fn package<C: Context>() -> ParentHandler<C> {
table.print_tty(false)?; table.print_tty(false)?;
Ok(()) Ok(())
}) })
.with_about("List information related to the lxc containers i.e. CPU, Memory, Disk") .with_about("about.list-lxc-container-info")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand("logs", logs::package_logs()) .subcommand("logs", logs::package_logs())
.subcommand( .subcommand(
"logs", "logs",
logs::package_logs().with_about("Display package logs"), logs::package_logs().with_about("about.display-package-logs"),
) )
.subcommand( .subcommand(
"logs", "logs",
from_fn_async(logs::cli_logs::<RpcContext, logs::PackageIdParams>) from_fn_async(logs::cli_logs::<RpcContext, logs::PackageIdParams>)
.no_display() .no_display()
.with_about("Display package logs"), .with_about("about.display-package-logs"),
) )
.subcommand( .subcommand(
"backup", "backup",
backup::package_backup::<C>() backup::package_backup::<C>().with_about("about.commands-restore-backup"),
.with_about("Commands for restoring package(s) from backup"),
) )
.subcommand( .subcommand(
"attach", "attach",
from_fn_async(service::attach) from_fn_async(service::attach)
.with_metadata("get_session", Value::Bool(true)) .with_metadata("get_session", Value::Bool(true))
.with_about("Execute commands within a service container") .with_about("about.execute-commands-container")
.no_cli(), .no_cli(),
) )
.subcommand("attach", from_fn_async(service::cli_attach).no_display()) .subcommand("attach", from_fn_async(service::cli_attach).no_display())
.subcommand( .subcommand(
"host", "host",
net::host::host_api::<C>().with_about("Manage network hosts for a package"), net::host::host_api::<C>().with_about("about.manage-network-hosts-package"),
) )
} }

View File

@@ -554,7 +554,7 @@ pub async fn journalctl(
let mut child = follow_cmd.stdout(Stdio::piped()).spawn()?; let mut child = follow_cmd.stdout(Stdio::piped()).spawn()?;
let out = let out =
BufReader::new(child.stdout.take().ok_or_else(|| { BufReader::new(child.stdout.take().ok_or_else(|| {
Error::new(eyre!("No stdout available"), crate::ErrorKind::Journald) Error::new(eyre!("{}", t!("logs.no-stdout-available")), crate::ErrorKind::Journald)
})?); })?);
let journalctl_entries = LinesStream::new(out.lines()); let journalctl_entries = LinesStream::new(out.lines());
@@ -700,7 +700,7 @@ pub async fn follow_logs<Context: AsRef<RpcContinuations>>(
RpcContinuation::ws( RpcContinuation::ws(
move |socket| async move { move |socket| async move {
if let Err(e) = ws_handler(first_entry, stream, socket).await { if let Err(e) = ws_handler(first_entry, stream, socket).await {
tracing::error!("Error in log stream: {}", e); tracing::error!("{}", t!("logs.error-in-log-stream", error = e.to_string()));
} }
}, },
Duration::from_secs(30), Duration::from_secs(30),

View File

@@ -141,7 +141,7 @@ impl LxcManager {
> 0 > 0
{ {
return Err(Error::new( return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"), eyre!("{}", t!("lxc.mod.rootfs-not-empty")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -249,6 +249,7 @@ impl LxcContainer {
.arg("-R") .arg("-R")
.arg("+C") .arg("+C")
.arg(&log_mount_point) .arg(&log_mount_point)
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::Filesystem) .invoke(ErrorKind::Filesystem)
.await .await
{ {
@@ -381,7 +382,7 @@ impl LxcContainer {
} }
if start.elapsed() > CONTAINER_DHCP_TIMEOUT { if start.elapsed() > CONTAINER_DHCP_TIMEOUT {
return Err(Error::new( return Err(Error::new(
eyre!("Timed out waiting for container to acquire DHCP lease"), eyre!("{}", t!("lxc.mod.dhcp-timeout")),
ErrorKind::Timeout, ErrorKind::Timeout,
)); ));
} }
@@ -407,9 +408,12 @@ impl LxcContainer {
if !output.status.success() { if !output.status.success() {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"Command failed with exit code: {:?} \n Message: {:?}", "{}",
output.status.code(), t!(
String::from_utf8(output.stderr) "lxc.mod.command-failed",
code = format!("{:?}", output.status.code()),
message = format!("{:?}", String::from_utf8(output.stderr))
)
), ),
ErrorKind::Docker, ErrorKind::Docker,
)); ));
@@ -437,7 +441,7 @@ impl LxcContainer {
> 0 > 0
{ {
return Err(Error::new( return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"), eyre!("{}", t!("lxc.mod.rootfs-not-empty")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -473,13 +477,19 @@ impl LxcContainer {
.await .await
); );
return Err(Error::new( return Err(Error::new(
eyre!("timed out waiting for socket"), eyre!("{}", t!("lxc.mod.socket-timeout")),
ErrorKind::Timeout, ErrorKind::Timeout,
)); ));
} }
tokio::time::sleep(Duration::from_millis(100)).await; tokio::time::sleep(Duration::from_millis(100)).await;
} }
tracing::info!("Connected to socket in {:?}", started.elapsed()); tracing::info!(
"{}",
t!(
"lxc.mod.connected-to-socket",
elapsed = format!("{:?}", started.elapsed())
)
);
Ok(UnixRpcClient::new(sock_path)) Ok(UnixRpcClient::new(sock_path))
} }
@@ -569,8 +579,11 @@ impl Drop for LxcContainer {
fn drop(&mut self) { fn drop(&mut self) {
if !self.exited { if !self.exited {
tracing::warn!( tracing::warn!(
"Container {} was ungracefully dropped. Cleaning up dangling containers...", "{}",
&**self.guid t!(
"lxc.mod.container-ungracefully-dropped",
container = &**self.guid
)
); );
let rootfs = self.rootfs.take(); let rootfs = self.rootfs.take();
let guid = std::mem::take(&mut self.guid); let guid = std::mem::take(&mut self.guid);
@@ -589,16 +602,25 @@ impl Drop for LxcContainer {
} }
.await .await
{ {
tracing::error!("Error reading logs from crashed container: {e}"); tracing::error!(
"{}",
t!("lxc.mod.error-reading-crashed-logs", error = e.to_string())
);
tracing::debug!("{e:?}") tracing::debug!("{e:?}")
} }
rootfs.unmount(true).await.log_err(); rootfs.unmount(true).await.log_err();
drop(guid); drop(guid);
if let Err(e) = manager.gc().await { if let Err(e) = manager.gc().await {
tracing::error!("Error cleaning up dangling LXC containers: {e}"); tracing::error!(
"{}",
t!(
"lxc.mod.error-cleaning-up-containers",
error = e.to_string()
)
);
tracing::debug!("{e:?}") tracing::debug!("{e:?}")
} else { } else {
tracing::info!("Successfully cleaned up dangling LXC containers"); tracing::info!("{}", t!("lxc.mod.cleaned-up-containers"));
} }
}); });
} }

View File

@@ -40,7 +40,7 @@ impl LocalAuthContext for RpcContext {
} }
fn unauthorized() -> Error { fn unauthorized() -> Error {
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) Error::new(eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization)
} }
async fn check_from_header<C: LocalAuthContext>(header: Option<&HeaderValue>) -> Result<(), Error> { async fn check_from_header<C: LocalAuthContext>(header: Option<&HeaderValue>) -> Result<(), Error> {

View File

@@ -146,7 +146,7 @@ impl HashSessionToken {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("UNAUTHORIZED"), eyre!("{}", t!("middleware.auth.unauthorized")),
crate::ErrorKind::Authorization, crate::ErrorKind::Authorization,
)) ))
} }
@@ -221,7 +221,7 @@ impl ValidSessionToken {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("UNAUTHORIZED"), eyre!("{}", t!("middleware.auth.unauthorized")),
crate::ErrorKind::Authorization, crate::ErrorKind::Authorization,
)) ))
} }
@@ -244,7 +244,7 @@ impl ValidSessionToken {
C::access_sessions(db) C::access_sessions(db)
.as_idx_mut(session_hash) .as_idx_mut(session_hash)
.ok_or_else(|| { .ok_or_else(|| {
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization) Error::new(eyre!("{}", t!("middleware.auth.unauthorized")), crate::ErrorKind::Authorization)
})? })?
.mutate(|s| { .mutate(|s| {
s.last_active = Utc::now(); s.last_active = Utc::now();
@@ -305,7 +305,7 @@ impl<C: SessionAuthContext> Middleware<C> for SessionAuth {
self.rate_limiter.mutate(|(count, time)| { self.rate_limiter.mutate(|(count, time)| {
if time.elapsed() < Duration::from_secs(20) && *count >= 3 { if time.elapsed() < Duration::from_secs(20) && *count >= 3 {
Err(Error::new( Err(Error::new(
eyre!("Please limit login attempts to 3 per 20 seconds."), eyre!("{}", t!("middleware.auth.rate-limited-login")),
crate::ErrorKind::RateLimited, crate::ErrorKind::RateLimited,
)) ))
} else { } else {

View File

@@ -90,7 +90,7 @@ impl SignatureAuthContext for RpcContext {
} }
Err(Error::new( Err(Error::new(
eyre!("Key is not authorized"), eyre!("{}", t!("middleware.auth.key-not-authorized")),
ErrorKind::IncorrectPassword, ErrorKind::IncorrectPassword,
)) ))
} }
@@ -141,7 +141,7 @@ impl SignatureAuth {
let mut cache = self.nonce_cache.lock().await; let mut cache = self.nonce_cache.lock().await;
if cache.values().any(|n| *n == nonce) { if cache.values().any(|n| *n == nonce) {
return Err(Error::new( return Err(Error::new(
eyre!("replay attack detected"), eyre!("{}", t!("middleware.auth.replay-attack-detected")),
ErrorKind::Authorization, ErrorKind::Authorization,
)); ));
} }
@@ -226,7 +226,7 @@ impl<C: SignatureAuthContext> Middleware<C> for SignatureAuth {
context.sig_context().await.into_iter().fold( context.sig_context().await.into_iter().fold(
Err(Error::new( Err(Error::new(
eyre!("no valid signature context available to verify"), eyre!("{}", t!("middleware.auth.no-valid-sig-context")),
ErrorKind::Authorization, ErrorKind::Authorization,
)), )),
|acc, x| { |acc, x| {
@@ -249,7 +249,7 @@ impl<C: SignatureAuthContext> Middleware<C> for SignatureAuth {
.unwrap_or_else(|e| e.duration().as_secs() as i64 * -1); .unwrap_or_else(|e| e.duration().as_secs() as i64 * -1);
if (now - commitment.timestamp).abs() > 30 { if (now - commitment.timestamp).abs() > 30 {
return Err(Error::new( return Err(Error::new(
eyre!("timestamp not within 30s of now"), eyre!("{}", t!("middleware.auth.timestamp-not-within-30s")),
ErrorKind::InvalidSignature, ErrorKind::InvalidSignature,
)); ));
} }
@@ -347,6 +347,6 @@ pub async fn call_remote<Ctx: SigningContext + AsRef<Client>>(
.with_kind(ErrorKind::Deserialization)? .with_kind(ErrorKind::Deserialization)?
.result .result
} }
_ => Err(Error::new(eyre!("unknown content type"), ErrorKind::Network).into()), _ => Err(Error::new(eyre!("{}", t!("middleware.auth.unknown-content-type")), ErrorKind::Network).into()),
} }
} }

View File

@@ -2,6 +2,7 @@ use axum::response::Response;
use http::HeaderValue; use http::HeaderValue;
use http::header::InvalidHeaderValue; use http::header::InvalidHeaderValue;
use rpc_toolkit::{Middleware, RpcRequest, RpcResponse}; use rpc_toolkit::{Middleware, RpcRequest, RpcResponse};
use rust_i18n::t;
use serde::Deserialize; use serde::Deserialize;
use crate::context::RpcContext; use crate::context::RpcContext;
@@ -46,7 +47,7 @@ impl Middleware<RpcContext> for SyncDb {
} }
.await .await
{ {
tracing::error!("error writing X-Patch-Sequence header: {e}"); tracing::error!("{}", t!("middleware.db.error-writing-patch-sequence-header", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
} }

View File

@@ -395,7 +395,7 @@ pub fn acme_api<C: Context>() -> ParentHandler<C> {
from_fn_async(init) from_fn_async(init)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Setup ACME certificate acquisition") .with_about("about.setup-acme-certificate-acquisition")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -403,7 +403,7 @@ pub fn acme_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove) from_fn_async(remove)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove ACME certificate acquisition configuration") .with_about("about.remove-acme-certificate-acquisition-configuration")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -54,13 +54,13 @@ pub fn dns_api<C: Context>() -> ParentHandler<C> {
Ok(()) Ok(())
}) })
.with_about("Test the DNS configuration for a domain"), .with_about("about.test-dns-configuration-for-domain"),
) )
.subcommand( .subcommand(
"set-static", "set-static",
from_fn_async(set_static_dns) from_fn_async(set_static_dns)
.no_display() .no_display()
.with_about("Set static DNS servers") .with_about("about.set-static-dns-servers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -88,7 +88,7 @@ pub fn dns_api<C: Context>() -> ParentHandler<C> {
Ok(()) Ok(())
}) })
.with_about("Dump address resolution table") .with_about("about.dump-address-resolution-table")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -292,7 +292,7 @@ impl Resolver {
.await .await
.map_err(|_| { .map_err(|_| {
Error::new( Error::new(
eyre!("timed out waiting to update dns catalog"), eyre!("{}", t!("net.dns.timeout-updating-catalog")),
ErrorKind::Timeout, ErrorKind::Timeout,
) )
})?; })?;
@@ -348,7 +348,13 @@ impl Resolver {
}) { }) {
return Some(res); return Some(res);
} else { } else {
tracing::warn!("Could not determine source interface of {src}"); tracing::warn!(
"{}",
t!(
"net.dns.could-not-determine-source-interface",
src = src.to_string()
)
);
} }
} }
if STARTOS.zone_of(name) || EMBASSY.zone_of(name) { if STARTOS.zone_of(name) || EMBASSY.zone_of(name) {
@@ -473,7 +479,10 @@ impl RequestHandler for Resolver {
Ok(Some(a)) => return a, Ok(Some(a)) => return a,
Ok(None) => (), Ok(None) => (),
Err(e) => { Err(e) => {
tracing::error!("Error resolving internal DNS: {e}"); tracing::error!(
"{}",
t!("net.dns.error-resolving-internal", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
let mut header = Header::response_from_request(request.header()); let mut header = Header::response_from_request(request.header());
header.set_recursion_available(true); header.set_recursion_available(true);
@@ -557,7 +566,7 @@ impl DnsController {
}) })
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("DNS Server Thread has exited"), eyre!("{}", t!("net.dns.server-thread-exited")),
crate::ErrorKind::Network, crate::ErrorKind::Network,
)) ))
} }
@@ -577,7 +586,7 @@ impl DnsController {
}) })
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("DNS Server Thread has exited"), eyre!("{}", t!("net.dns.server-thread-exited")),
crate::ErrorKind::Network, crate::ErrorKind::Network,
)) ))
} }
@@ -598,7 +607,7 @@ impl DnsController {
}) })
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("DNS Server Thread has exited"), eyre!("{}", t!("net.dns.server-thread-exited")),
crate::ErrorKind::Network, crate::ErrorKind::Network,
)) ))
} }
@@ -624,7 +633,7 @@ impl DnsController {
}) })
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("DNS Server Thread has exited"), eyre!("{}", t!("net.dns.server-thread-exited")),
crate::ErrorKind::Network, crate::ErrorKind::Network,
)) ))
} }

View File

@@ -34,7 +34,7 @@ impl AvailablePorts {
pub fn alloc(&mut self) -> Result<u16, Error> { pub fn alloc(&mut self) -> Result<u16, Error> {
self.0.request_id().ok_or_else(|| { self.0.request_id().ok_or_else(|| {
Error::new( Error::new(
eyre!("No more dynamic ports available!"), eyre!("{}", t!("net.forward.no-dynamic-ports-available")),
ErrorKind::Network, ErrorKind::Network,
) )
}) })
@@ -240,7 +240,7 @@ impl PortForwardController {
} }
.await .await
{ {
tracing::error!("error initializing PortForwardController: {e:#}"); tracing::error!("{}", t!("net.forward.error-initializing-controller", error = format!("{e:#}")));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
tokio::time::sleep(Duration::from_secs(5)).await; tokio::time::sleep(Duration::from_secs(5)).await;
} }
@@ -400,7 +400,7 @@ impl InterfaceForwardEntry {
) -> Result<Arc<()>, Error> { ) -> Result<Arc<()>, Error> {
if external != self.external { if external != self.external {
return Err(Error::new( return Err(Error::new(
eyre!("Mismatched external port in InterfaceForwardEntry"), eyre!("{}", t!("net.forward.mismatched-external-port")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -477,7 +477,7 @@ impl InterfaceForwardState {
fn err_has_exited<T>(_: T) -> Error { fn err_has_exited<T>(_: T) -> Error {
Error::new( Error::new(
eyre!("PortForwardController thread has exited"), eyre!("{}", t!("net.forward.controller-thread-exited")),
ErrorKind::Unknown, ErrorKind::Unknown,
) )
} }

View File

@@ -95,7 +95,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
Ok(()) Ok(())
}) })
.with_about("Show gateways StartOS can listen on") .with_about("about.show-gateways-startos-can-listen-on")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -103,7 +103,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
from_fn_async(set_public) from_fn_async(set_public)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Indicate whether this gateway has inbound access from the WAN") .with_about("about.indicate-gateway-inbound-access-from-wan")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -111,10 +111,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
from_fn_async(unset_public) from_fn_async(unset_public)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about(concat!( .with_about("about.allow-gateway-infer-inbound-access-from-wan")
"Allow this gateway to infer whether it has",
" inbound access from the WAN based on its IPv4 address"
))
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -122,7 +119,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
from_fn_async(forget_iface) from_fn_async(forget_iface)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Forget a disconnected gateway") .with_about("about.forget-disconnected-gateway")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -130,7 +127,7 @@ pub fn gateway_api<C: Context>() -> ParentHandler<C> {
from_fn_async(set_name) from_fn_async(set_name)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Rename a gateway") .with_about("about.rename-gateway")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -464,7 +461,8 @@ async fn watcher(
ensure_code!( ensure_code!(
!devices.is_empty(), !devices.is_empty(),
ErrorKind::Network, ErrorKind::Network,
"NetworkManager returned no devices. Trying again..." "{}",
t!("net.gateway.no-devices-returned")
); );
let mut ifaces = BTreeSet::new(); let mut ifaces = BTreeSet::new();
let mut jobs = Vec::new(); let mut jobs = Vec::new();
@@ -731,7 +729,8 @@ async fn watch_ip(
Ok(a) => a, Ok(a) => a,
Err(e) => { Err(e) => {
tracing::error!( tracing::error!(
"Failed to determine WAN IP for {iface}: {e}" "{}",
t!("net.gateway.failed-to-determine-wan-ip", iface = iface.to_string(), error = e.to_string())
); );
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
None None
@@ -1021,7 +1020,13 @@ impl NetworkInterfaceController {
info info
} }
Err(e) => { Err(e) => {
tracing::error!("Error loading network interface info: {e}"); tracing::error!(
"{}",
t!(
"net.gateway.error-loading-interface-info",
error = e.to_string()
)
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
OrdMap::new() OrdMap::new()
} }
@@ -1050,7 +1055,10 @@ impl NetworkInterfaceController {
} }
.await .await
{ {
tracing::error!("Error syncing ip info to db: {e}"); tracing::error!(
"{}",
t!("net.gateway.error-syncing-ip-info", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
@@ -1060,7 +1068,10 @@ impl NetworkInterfaceController {
} }
.await; .await;
if let Err(e) = res { if let Err(e) = res {
tracing::error!("Error syncing ip info to db: {e}"); tracing::error!(
"{}",
t!("net.gateway.error-syncing-ip-info", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
}) })
@@ -1121,7 +1132,7 @@ impl NetworkInterfaceController {
.map_or(false, |i| i.ip_info.is_some()) .map_or(false, |i| i.ip_info.is_some())
{ {
err = Some(Error::new( err = Some(Error::new(
eyre!("Cannot forget currently connected interface"), eyre!("{}", t!("net.gateway.cannot-forget-connected-interface")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
return false; return false;
@@ -1167,7 +1178,7 @@ impl NetworkInterfaceController {
if &*ac == "/" { if &*ac == "/" {
return Err(Error::new( return Err(Error::new(
eyre!("Cannot delete device without active connection"), eyre!("{}", t!("net.gateway.cannot-delete-without-connection")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }

View File

@@ -120,7 +120,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Add a public domain to this host") .with_about("about.add-public-domain-to-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -129,7 +129,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Remove a public domain from this host") .with_about("about.remove-public-domain-from-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.with_inherited(|_, a| a), .with_inherited(|_, a| a),
@@ -143,7 +143,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Add a private domain to this host") .with_about("about.add-private-domain-to-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -152,7 +152,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Remove a private domain from this host") .with_about("about.remove-private-domain-from-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.with_inherited(|_, a| a), .with_inherited(|_, a| a),
@@ -168,7 +168,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Add an address to this host") .with_about("about.add-address-to-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -177,7 +177,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|_, a| a) .with_inherited(|_, a| a)
.no_display() .no_display()
.with_about("Remove an address from this host") .with_about("about.remove-address-from-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.with_inherited(Kind::inheritance), .with_inherited(Kind::inheritance),
@@ -230,7 +230,7 @@ pub fn address_api<C: Context, Kind: HostApiKind>()
Ok(()) Ok(())
}) })
.with_about("List addresses for this host") .with_about("about.list-addresses-for-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -209,7 +209,7 @@ pub fn binding<C: Context, Kind: HostApiKind>()
Ok(()) Ok(())
}) })
.with_about("List bindinges for this host") .with_about("about.list-bindings-for-host")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -218,7 +218,7 @@ pub fn binding<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(Kind::inheritance) .with_inherited(Kind::inheritance)
.no_display() .no_display()
.with_about("Set whether this gateway should be enabled for this binding") .with_about("about.set-gateway-enabled-for-binding")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -243,7 +243,7 @@ pub fn host_api<C: Context>() -> ParentHandler<C, RequiresPackageId> {
} }
Ok(()) Ok(())
}) })
.with_about("List host IDs available for this service"), .with_about("about.list-host-ids-for-service"),
) )
.subcommand( .subcommand(
"address", "address",

View File

@@ -23,32 +23,29 @@ pub mod wifi;
pub fn net_api<C: Context>() -> ParentHandler<C> { pub fn net_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
.subcommand( .subcommand("tor", tor::tor_api::<C>().with_about("about.tor-commands"))
"tor",
tor::tor_api::<C>().with_about("Tor commands such as list-services, logs, and reset"),
)
.subcommand( .subcommand(
"acme", "acme",
acme::acme_api::<C>().with_about("Setup automatic clearnet certificate acquisition"), acme::acme_api::<C>().with_about("about.setup-acme-certificate"),
) )
.subcommand( .subcommand(
"dns", "dns",
dns::dns_api::<C>().with_about("Manage and query DNS"), dns::dns_api::<C>().with_about("about.manage-query-dns"),
) )
.subcommand( .subcommand(
"forward", "forward",
forward::forward_api::<C>().with_about("Manage port forwards"), forward::forward_api::<C>().with_about("about.manage-port-forwards"),
) )
.subcommand( .subcommand(
"gateway", "gateway",
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"), gateway::gateway_api::<C>().with_about("about.view-edit-gateway-configs"),
) )
.subcommand( .subcommand(
"tunnel", "tunnel",
tunnel::tunnel_api::<C>().with_about("Manage tunnels"), tunnel::tunnel_api::<C>().with_about("about.manage-tunnels"),
) )
.subcommand( .subcommand(
"vhost", "vhost",
vhost::vhost_api::<C>().with_about("Manage ssl virtual host proxy"), vhost::vhost_api::<C>().with_about("about.manage-ssl-vhost-proxy"),
) )
} }

View File

@@ -170,7 +170,7 @@ impl FullchainCertData {
] ]
.into_iter() .into_iter()
.min() .min()
.ok_or_else(|| Error::new(eyre!("unreachable"), ErrorKind::Unknown)) .ok_or_else(|| Error::new(eyre!("{}", t!("net.ssl.unreachable")), ErrorKind::Unknown))
} }
} }

View File

@@ -107,7 +107,10 @@ impl TorSecretKey {
Ok(Self( Ok(Self(
tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes(bytes) tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes(bytes)
.ok_or_else(|| { .ok_or_else(|| {
Error::new(eyre!("invalid ed25519 expanded secret key"), ErrorKind::Tor) Error::new(
eyre!("{}", t!("net.tor.invalid-ed25519-key")),
ErrorKind::Tor,
)
})? })?
.into(), .into(),
)) ))
@@ -226,19 +229,19 @@ pub fn tor_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_services) from_fn_async(list_services)
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_services(handle.params, result)) .with_custom_display_fn(|handle, result| display_services(handle.params, result))
.with_about("Display Tor V3 Onion Addresses") .with_about("about.display-tor-v3-onion-addresses")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"reset", "reset",
from_fn_async(reset) from_fn_async(reset)
.no_display() .no_display()
.with_about("Reset Tor daemon") .with_about("about.reset-tor-daemon")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"key", "key",
key::<C>().with_about("Manage the onion service key store"), key::<C>().with_about("about.manage-onion-service-key-store"),
) )
} }
@@ -247,13 +250,13 @@ pub fn key<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"generate", "generate",
from_fn_async(generate_key) from_fn_async(generate_key)
.with_about("Generate an onion service key and add it to the key store") .with_about("about.generate-onion-service-key-add-to-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"add", "add",
from_fn_async(add_key) from_fn_async(add_key)
.with_about("Add an onion service key to the key store") .with_about("about.add-onion-service-key-to-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -265,7 +268,7 @@ pub fn key<C: Context>() -> ParentHandler<C> {
} }
Ok(()) Ok(())
}) })
.with_about("List onion services with keys in the key store") .with_about("about.list-onion-services-with-keys-in-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -447,9 +450,13 @@ impl TorController {
if prev_inst.elapsed() > BOOTSTRAP_PROGRESS_TIMEOUT { if prev_inst.elapsed() > BOOTSTRAP_PROGRESS_TIMEOUT {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"Bootstrap has not made progress for {}", "{}",
crate::util::serde::Duration::from( t!(
BOOTSTRAP_PROGRESS_TIMEOUT "net.tor.bootstrap-no-progress",
duration = crate::util::serde::Duration::from(
BOOTSTRAP_PROGRESS_TIMEOUT
)
.to_string()
) )
), ),
ErrorKind::Tor, ErrorKind::Tor,
@@ -466,7 +473,10 @@ impl TorController {
res = bootstrap_fut => res, res = bootstrap_fut => res,
res = failure_fut => res, res = failure_fut => res,
} { } {
tracing::error!("Tor Bootstrap Error: {e}"); tracing::error!(
"{}",
t!("net.tor.bootstrap-error", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} else { } else {
bootstrapper_client.send_modify(|_| ()); bootstrapper_client.send_modify(|_| ());
@@ -516,7 +526,13 @@ impl TorController {
} }
.await .await
{ {
tracing::error!("Tor Health Error: {e}"); tracing::error!(
"{}",
t!(
"net.tor.health-error",
error = e.to_string()
)
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
}); });
@@ -529,7 +545,10 @@ impl TorController {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("status event stream ended"), eyre!(
"{}",
t!("net.tor.status-stream-ended")
),
ErrorKind::Tor, ErrorKind::Tor,
)) ))
}) })
@@ -560,13 +579,19 @@ impl TorController {
} }
.await .await
{ {
tracing::error!("Tor Client Health Error: {e}"); tracing::error!(
"{}",
t!("net.tor.client-health-error", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
} }
tracing::error!( tracing::error!(
"Client failed health check {} times, recycling", "{}",
HEALTH_CHECK_FAILURE_ALLOWANCE t!(
"net.tor.health-check-failed-recycling",
count = HEALTH_CHECK_FAILURE_ALLOWANCE
)
); );
} }
@@ -574,7 +599,10 @@ impl TorController {
}) })
.await .await
{ {
tracing::error!("Tor Bootstrapper Error: {e}"); tracing::error!(
"{}",
t!("net.tor.bootstrapper-error", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
if let Err::<(), Error>(e) = async { if let Err::<(), Error>(e) = async {
@@ -592,7 +620,10 @@ impl TorController {
} }
.await .await
{ {
tracing::error!("Tor Client Creation Error: {e}"); tracing::error!(
"{}",
t!("net.tor.client-creation-error", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
} }
@@ -684,7 +715,10 @@ impl TorController {
.await .await
.with_kind(ErrorKind::Network)?; .with_kind(ErrorKind::Network)?;
if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) { if let Err(e) = socket2::SockRef::from(&tcp_stream).set_keepalive(true) {
tracing::error!("Failed to set tcp keepalive: {e}"); tracing::error!(
"{}",
t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string())
);
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
Ok(Box::new(tcp_stream)) Ok(Box::new(tcp_stream))
@@ -812,7 +846,7 @@ impl OnionService {
.await .await
.with_kind(ErrorKind::Network)?; .with_kind(ErrorKind::Network)?;
if let Err(e) = socket2::SockRef::from(&outgoing).set_keepalive(true) { if let Err(e) = socket2::SockRef::from(&outgoing).set_keepalive(true) {
tracing::error!("Failed to set tcp keepalive: {e}"); tracing::error!("{}", t!("net.tor.failed-to-set-tcp-keepalive", error = e.to_string()));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
let mut incoming = req let mut incoming = req
@@ -852,7 +886,7 @@ impl OnionService {
} }
.await .await
{ {
tracing::error!("Tor Client Error: {e}"); tracing::error!("{}", t!("net.tor.client-error", error = e.to_string()));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
} }
} }

View File

@@ -249,26 +249,26 @@ pub fn tor_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_services) from_fn_async(list_services)
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_services(handle.params, result)) .with_custom_display_fn(|handle, result| display_services(handle.params, result))
.with_about("Display Tor V3 Onion Addresses") .with_about("about.display-tor-v3-onion-addresses")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand("logs", logs().with_about("Display Tor logs")) .subcommand("logs", logs().with_about("about.display-tor-logs"))
.subcommand( .subcommand(
"logs", "logs",
from_fn_async(crate::logs::cli_logs::<RpcContext, Empty>) from_fn_async(crate::logs::cli_logs::<RpcContext, Empty>)
.no_display() .no_display()
.with_about("Display Tor logs"), .with_about("about.display-tor-logs"),
) )
.subcommand( .subcommand(
"reset", "reset",
from_fn_async(reset) from_fn_async(reset)
.no_display() .no_display()
.with_about("Reset Tor daemon") .with_about("about.reset-tor-daemon")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"key", "key",
key::<C>().with_about("Manage the onion service key store"), key::<C>().with_about("about.manage-onion-service-key-store"),
) )
} }
@@ -277,13 +277,13 @@ pub fn key<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"generate", "generate",
from_fn_async(generate_key) from_fn_async(generate_key)
.with_about("Generate an onion service key and add it to the key store") .with_about("about.generate-onion-service-key-add-to-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"add", "add",
from_fn_async(add_key) from_fn_async(add_key)
.with_about("Add an onion service key to the key store") .with_about("about.add-onion-service-key-to-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -295,7 +295,7 @@ pub fn key<C: Context>() -> ParentHandler<C> {
} }
Ok(()) Ok(())
}) })
.with_about("List onion services with keys in the key store") .with_about("about.list-onion-services-with-keys-in-store")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -19,14 +19,14 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"add", "add",
from_fn_async(add_tunnel) from_fn_async(add_tunnel)
.with_about("Add a new tunnel") .with_about("about.add-new-tunnel")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove_tunnel) from_fn_async(remove_tunnel)
.no_display() .no_display()
.with_about("Remove a tunnel") .with_about("about.remove-tunnel")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -30,7 +30,7 @@ type WifiManager = Arc<RwLock<Option<WpaCli>>>;
// Ok(wifi_manager) // Ok(wifi_manager)
// } else { // } else {
// Err(Error::new( // Err(Error::new(
// color_eyre::eyre::eyre!("No WiFi interface available"), // color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
// ErrorKind::Wifi, // ErrorKind::Wifi,
// )) // ))
// } // }
@@ -42,28 +42,28 @@ pub fn wifi<C: Context>() -> ParentHandler<C> {
"set-enabled", "set-enabled",
from_fn_async(set_enabled) from_fn_async(set_enabled)
.no_display() .no_display()
.with_about("Enable or disable wifi") .with_about("about.enable-disable-wifi")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"add", "add",
from_fn_async(add) from_fn_async(add)
.no_display() .no_display()
.with_about("Add wifi ssid and password") .with_about("about.add-wifi-ssid-password")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"connect", "connect",
from_fn_async(connect) from_fn_async(connect)
.no_display() .no_display()
.with_about("Connect to wifi network") .with_about("about.connect-wifi-network")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove) from_fn_async(remove)
.no_display() .no_display()
.with_about("Remove a wifi network") .with_about("about.remove-wifi-network")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -71,16 +71,16 @@ pub fn wifi<C: Context>() -> ParentHandler<C> {
from_fn_async(get) from_fn_async(get)
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_wifi_info(handle.params, result)) .with_custom_display_fn(|handle, result| display_wifi_info(handle.params, result))
.with_about("List wifi info") .with_about("about.list-wifi-info")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"country", "country",
country::<C>().with_about("Command to set country"), country::<C>().with_about("about.command-set-country"),
) )
.subcommand( .subcommand(
"available", "available",
available::<C>().with_about("Command to list available wifi networks"), available::<C>().with_about("about.command-list-available-wifi"),
) )
} }
@@ -133,7 +133,7 @@ pub fn available<C: Context>() -> ParentHandler<C> {
from_fn_async(get_available) from_fn_async(get_available)
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_wifi_list(handle.params, result)) .with_custom_display_fn(|handle, result| display_wifi_list(handle.params, result))
.with_about("List available wifi networks") .with_about("about.list-available-wifi-networks")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -143,7 +143,7 @@ pub fn country<C: Context>() -> ParentHandler<C> {
"set", "set",
from_fn_async(set_country) from_fn_async(set_country)
.no_display() .no_display()
.with_about("Set Country") .with_about("about.set-country")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -160,13 +160,13 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re
let wifi_manager = ctx.wifi_manager.clone(); let wifi_manager = ctx.wifi_manager.clone();
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
if !password.is_ascii() { if !password.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("WiFi Password may not have special characters"), color_eyre::eyre::eyre!("{}", t!("net.wifi.password-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -176,11 +176,11 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re
ssid: &Ssid, ssid: &Ssid,
password: &Psk, password: &Psk,
) -> Result<(), Error> { ) -> Result<(), Error> {
tracing::info!("Adding new WiFi network: '{}'", ssid.0); tracing::info!("{}", t!("net.wifi.adding-network", ssid = &ssid.0));
let mut wpa_supplicant = wifi_manager.write_owned().await; let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
@@ -195,10 +195,17 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re
) )
.await .await
{ {
tracing::error!("Failed to add new WiFi network '{}': {}", ssid, err); tracing::error!(
"{}",
t!(
"net.wifi.failed-to-add-network",
ssid = &ssid,
error = err.to_string()
)
);
tracing::debug!("{:?}", err); tracing::debug!("{:?}", err);
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("Failed adding {}", ssid), color_eyre::eyre::eyre!("{}", t!("net.wifi.failed-adding", ssid = &ssid)),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -230,7 +237,7 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result
let wifi_manager = ctx.wifi_manager.clone(); let wifi_manager = ctx.wifi_manager.clone();
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -242,19 +249,19 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result
let mut wpa_supplicant = wifi_manager.write_owned().await; let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
let current = wpa_supplicant.get_current_network().await?; let current = wpa_supplicant.get_current_network().await?;
let connected = wpa_supplicant.select_network(db.clone(), ssid).await?; let connected = wpa_supplicant.select_network(db.clone(), ssid).await?;
if connected { if connected {
tracing::info!("Successfully connected to WiFi: '{}'", ssid.0); tracing::info!("{}", t!("net.wifi.connected-successfully", ssid = &ssid.0));
} else { } else {
tracing::info!("Failed to connect to WiFi: '{}'", ssid.0); tracing::info!("{}", t!("net.wifi.connection-failed", ssid = &ssid.0));
match current { match current {
None => { None => {
tracing::info!("No WiFi to revert to!"); tracing::info!("{}", t!("net.wifi.no-wifi-to-revert"));
} }
Some(current) => { Some(current) => {
wpa_supplicant.select_network(db, &current).await?; wpa_supplicant.select_network(db, &current).await?;
@@ -267,9 +274,16 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result
if let Err(err) = if let Err(err) =
connect_procedure(ctx.db.clone(), wifi_manager.clone(), &Ssid(ssid.clone())).await connect_procedure(ctx.db.clone(), wifi_manager.clone(), &Ssid(ssid.clone())).await
{ {
tracing::error!("Failed to connect to WiFi network '{}': {}", &ssid, err); tracing::error!(
"{}",
t!(
"net.wifi.failed-to-connect",
ssid = &ssid,
error = err.to_string()
)
);
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("Can't connect to {}", ssid), color_eyre::eyre::eyre!("{}", t!("net.wifi.cant-connect", ssid = &ssid)),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -297,7 +311,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
let wifi_manager = ctx.wifi_manager.clone(); let wifi_manager = ctx.wifi_manager.clone();
if !ssid.is_ascii() { if !ssid.is_ascii() {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("SSID may not have special characters"), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-no-special-characters")),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -305,7 +319,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
let mut wpa_supplicant = wifi_manager.write_owned().await; let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
@@ -316,9 +330,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
is_current_being_removed && !interface_connected(&ctx.ethernet_interface).await?; is_current_being_removed && !interface_connected(&ctx.ethernet_interface).await?;
if is_current_removed_and_no_hardwire { if is_current_removed_and_no_hardwire {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!( color_eyre::eyre::eyre!("{}", t!("net.wifi.forbidden-delete-would-disconnect")),
"Forbidden: Deleting this network would make your server unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."
),
ErrorKind::Wifi, ErrorKind::Wifi,
)); ));
} }
@@ -463,7 +475,7 @@ pub async fn get(ctx: RpcContext, _: Empty) -> Result<WifiListInfo, Error> {
let wpa_supplicant = wifi_manager.read_owned().await; let wpa_supplicant = wifi_manager.read_owned().await;
let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
@@ -517,7 +529,7 @@ pub async fn get_available(ctx: RpcContext, _: Empty) -> Result<Vec<WifiListOut>
let wpa_supplicant = wifi_manager.read_owned().await; let wpa_supplicant = wifi_manager.read_owned().await;
let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
@@ -558,14 +570,14 @@ pub async fn set_country(
let wifi_manager = ctx.wifi_manager.clone(); let wifi_manager = ctx.wifi_manager.clone();
if !interface_connected(&ctx.ethernet_interface).await? { if !interface_connected(&ctx.ethernet_interface).await? {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("Won't change country without hardwire connection"), color_eyre::eyre::eyre!("{}", t!("net.wifi.wont-change-country-without-ethernet")),
crate::ErrorKind::Wifi, crate::ErrorKind::Wifi,
)); ));
} }
let mut wpa_supplicant = wifi_manager.write_owned().await; let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| { let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"), color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
@@ -684,7 +696,14 @@ impl WpaCli {
.await .await
.map(|_| ()) .map(|_| ())
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
tracing::warn!("Failed to set interface {} for {}", self.interface, ssid.0); tracing::warn!(
"{}",
t!(
"net.wifi.failed-to-set-interface",
interface = &self.interface,
ssid = &ssid.0
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
}); });
Command::new("nmcli") Command::new("nmcli")
@@ -719,13 +738,13 @@ impl WpaCli {
} }
let first_country = r.lines().find(|s| s.contains("country")).ok_or_else(|| { let first_country = r.lines().find(|s| s.contains("country")).ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Could not find a country config lines"), color_eyre::eyre::eyre!("{}", t!("net.wifi.could-not-find-country-config")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?; })?;
let country = &RE.captures(first_country).ok_or_else(|| { let country = &RE.captures(first_country).ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Could not find a country config with regex"), color_eyre::eyre::eyre!("{}", t!("net.wifi.could-not-parse-country-config")),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?[1]; })?[1];
@@ -734,7 +753,10 @@ impl WpaCli {
} else { } else {
Ok(Some(CountryCode::for_alpha2(country).map_err(|_| { Ok(Some(CountryCode::for_alpha2(country).map_err(|_| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Invalid Country Code: {}", country), color_eyre::eyre::eyre!(
"{}",
t!("net.wifi.invalid-country-code", country = country)
),
ErrorKind::Wifi, ErrorKind::Wifi,
) )
})?)) })?))
@@ -877,7 +899,7 @@ impl WpaCli {
let m_id = self.check_active_network(ssid).await?; let m_id = self.check_active_network(ssid).await?;
match m_id { match m_id {
None => Err(Error::new( None => Err(Error::new(
color_eyre::eyre::eyre!("SSID Not Found"), color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-not-found")),
ErrorKind::Wifi, ErrorKind::Wifi,
)), )),
Some(x) => { Some(x) => {
@@ -1058,7 +1080,7 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
if let Some(last_country_code) = wifi.last_region { if let Some(last_country_code) = wifi.last_region {
tracing::info!("Setting the region"); tracing::info!("{}", t!("net.wifi.setting-region"));
let _ = Command::new("iw") let _ = Command::new("iw")
.arg("reg") .arg("reg")
.arg("set") .arg("set")
@@ -1066,7 +1088,7 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
.invoke(ErrorKind::Wifi) .invoke(ErrorKind::Wifi)
.await?; .await?;
} else { } else {
tracing::info!("Setting the region fallback"); tracing::info!("{}", t!("net.wifi.setting-region-fallback"));
let _ = Command::new("iw") let _ = Command::new("iw")
.arg("reg") .arg("reg")
.arg("set") .arg("set")

View File

@@ -27,49 +27,49 @@ pub fn notification<C: Context>() -> ParentHandler<C> {
"list", "list",
from_fn_async(list) from_fn_async(list)
.with_display_serializable() .with_display_serializable()
.with_about("List notifications") .with_about("about.list-notifications")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove) from_fn_async(remove)
.no_display() .no_display()
.with_about("Remove notification for given ids") .with_about("about.remove-notification-for-ids")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove-before", "remove-before",
from_fn_async(remove_before) from_fn_async(remove_before)
.no_display() .no_display()
.with_about("Remove notifications preceding a given id") .with_about("about.remove-notifications-before-id")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"mark-seen", "mark-seen",
from_fn_async(mark_seen) from_fn_async(mark_seen)
.no_display() .no_display()
.with_about("Mark given notifications as seen") .with_about("about.mark-notifications-seen")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"mark-seen-before", "mark-seen-before",
from_fn_async(mark_seen_before) from_fn_async(mark_seen_before)
.no_display() .no_display()
.with_about("Mark notifications preceding a given id as seen") .with_about("about.mark-notifications-seen-before-id")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"mark-unseen", "mark-unseen",
from_fn_async(mark_unseen) from_fn_async(mark_unseen)
.no_display() .no_display()
.with_about("Mark given notifications as unseen") .with_about("about.mark-notifications-unseen")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"create", "create",
from_fn_async(create) from_fn_async(create)
.no_display() .no_display()
.with_about("Persist a newly created notification") .with_about("about.persist-new-notification")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -346,7 +346,7 @@ pub struct InvalidNotificationLevel(String);
impl From<InvalidNotificationLevel> for crate::Error { impl From<InvalidNotificationLevel> for crate::Error {
fn from(val: InvalidNotificationLevel) -> Self { fn from(val: InvalidNotificationLevel) -> Self {
Error::new( Error::new(
eyre!("Invalid Notification Level: {}", val.0), eyre!("{}", t!("notifications.invalid-level", level = val.0)),
ErrorKind::ParseDbField, ErrorKind::ParseDbField,
) )
} }

View File

@@ -1,6 +1,7 @@
pub use color_eyre::eyre::eyre; pub use color_eyre::eyre::eyre;
pub use imbl_value::InternedString; pub use imbl_value::InternedString;
pub use lazy_format::lazy_format; pub use lazy_format::lazy_format;
pub use rust_i18n::t;
pub use tracing::instrument; pub use tracing::instrument;
pub use crate::db::prelude::*; pub use crate::db::prelude::*;

View File

@@ -21,7 +21,7 @@ pub fn admin_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
.subcommand( .subcommand(
"signer", "signer",
signers_api::<C>().with_about("Commands to add or list signers"), signers_api::<C>().with_about("about.commands-add-list-signers"),
) )
.subcommand( .subcommand(
"add", "add",
@@ -33,14 +33,14 @@ pub fn admin_api<C: Context>() -> ParentHandler<C> {
"add", "add",
from_fn_async(cli_add_admin) from_fn_async(cli_add_admin)
.no_display() .no_display()
.with_about("Add admin signer"), .with_about("about.add-admin-signer"),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove_admin) from_fn_async(remove_admin)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove an admin signer") .with_about("about.remove-admin-signer")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -49,7 +49,7 @@ pub fn admin_api<C: Context>() -> ParentHandler<C> {
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_signers(handle.params, result)) .with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List admin signers") .with_about("about.list-admin-signers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -62,7 +62,7 @@ fn signers_api<C: Context>() -> ParentHandler<C> {
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_signers(handle.params, result)) .with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List signers") .with_about("about.list-signers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -73,7 +73,7 @@ fn signers_api<C: Context>() -> ParentHandler<C> {
) )
.subcommand( .subcommand(
"add", "add",
from_fn_async(cli_add_signer).with_about("Add signer"), from_fn_async(cli_add_signer).with_about("about.add-signer"),
) )
.subcommand( .subcommand(
"edit", "edit",
@@ -93,7 +93,7 @@ impl Model<BTreeMap<Guid, SignerInfo>> {
.next() .next()
.transpose()? .transpose()?
.map(|(a, _)| a) .map(|(a, _)| a)
.ok_or_else(|| Error::new(eyre!("unknown signer"), ErrorKind::Authorization)) .ok_or_else(|| Error::new(eyre!("{}", t!("registry.admin.unknown-signer")), ErrorKind::Authorization))
} }
pub fn get_signer_info(&self, key: &AnyVerifyingKey) -> Result<(Guid, SignerInfo), Error> { pub fn get_signer_info(&self, key: &AnyVerifyingKey) -> Result<(Guid, SignerInfo), Error> {
@@ -103,7 +103,7 @@ impl Model<BTreeMap<Guid, SignerInfo>> {
.filter_ok(|(_, s)| s.keys.contains(key)) .filter_ok(|(_, s)| s.keys.contains(key))
.next() .next()
.transpose()? .transpose()?
.ok_or_else(|| Error::new(eyre!("unknown signer"), ErrorKind::Authorization)) .ok_or_else(|| Error::new(eyre!("{}", t!("registry.admin.unknown-signer")), ErrorKind::Authorization))
} }
pub fn add_signer(&mut self, signer: &SignerInfo) -> Result<Guid, Error> { pub fn add_signer(&mut self, signer: &SignerInfo) -> Result<Guid, Error> {
@@ -117,9 +117,8 @@ impl Model<BTreeMap<Guid, SignerInfo>> {
{ {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"A signer {} ({}) already exists with a matching key", "{}",
guid, t!("registry.admin.signer-already-exists", guid = guid, name = s.name)
s.name
), ),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));

View File

@@ -49,7 +49,7 @@ impl<Commitment> RegistryAsset<Commitment> {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("Failed to load any http url"), eyre!("{}", t!("registry.asset.failed-to-load-http-url")),
ErrorKind::Network, ErrorKind::Network,
)) ))
} }
@@ -64,7 +64,7 @@ impl<Commitment> RegistryAsset<Commitment> {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("Failed to load any http url"), eyre!("{}", t!("registry.asset.failed-to-load-http-url")),
ErrorKind::Network, ErrorKind::Network,
)) ))
} }
@@ -80,7 +80,7 @@ impl<Commitment> RegistryAsset<Commitment> {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("Failed to load any http url"), eyre!("{}", t!("registry.asset.failed-to-load-http-url")),
ErrorKind::Network, ErrorKind::Network,
)) ))
} }

View File

@@ -124,7 +124,7 @@ impl RegistryContext {
}; };
if config.registry_hostname.is_empty() { if config.registry_hostname.is_empty() {
return Err(Error::new( return Err(Error::new(
eyre!("missing required configuration: registry-hostname"), eyre!("{}", t!("registry.context.missing-hostname")),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
} }
@@ -195,7 +195,7 @@ impl CallRemote<RegistryContext> for CliContext {
url url
} else { } else {
return Err( return Err(
Error::new(eyre!("`--registry` required"), ErrorKind::InvalidRequest).into(), Error::new(eyre!("{}", t!("registry.context.registry-required")), ErrorKind::InvalidRequest).into(),
); );
}; };
@@ -330,7 +330,7 @@ impl SignatureAuthContext for RegistryContext {
} }
} }
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.context.unauthorized")), ErrorKind::Authorization))
} }
async fn post_auth_hook( async fn post_auth_hook(
&self, &self,

View File

@@ -22,7 +22,7 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
"dump", "dump",
from_fn_async(cli_dump) from_fn_async(cli_dump)
.with_display_serializable() .with_display_serializable()
.with_about("Filter/query db to display tables and records"), .with_about("about.filter-query-db"),
) )
.subcommand( .subcommand(
"dump", "dump",
@@ -34,7 +34,7 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
"apply", "apply",
from_fn_async(cli_apply) from_fn_async(cli_apply)
.no_display() .no_display()
.with_about("Update a db record"), .with_about("about.update-db-record"),
) )
.subcommand( .subcommand(
"apply", "apply",

View File

@@ -21,7 +21,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
from_fn_async(get_info) from_fn_async(get_info)
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_display_serializable() .with_display_serializable()
.with_about("Display registry name, icon, and package categories") .with_about("about.display-registry-info")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -29,7 +29,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
from_fn_async(set_name) from_fn_async(set_name)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Set the name for the registry") .with_about("about.set-registry-name")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -42,7 +42,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
"set-icon", "set-icon",
from_fn_async(cli_set_icon) from_fn_async(cli_set_icon)
.no_display() .no_display()
.with_about("Set the icon for the registry"), .with_about("about.set-registry-icon"),
) )
} }

View File

@@ -76,26 +76,26 @@ pub fn registry_api<C: Context>() -> ParentHandler<C> {
"index", "index",
from_fn_async(get_full_index) from_fn_async(get_full_index)
.with_display_serializable() .with_display_serializable()
.with_about("List info including registry name and packages") .with_about("about.list-registry-info-packages")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand("info", info::info_api::<C>()) .subcommand("info", info::info_api::<C>())
// set info and categories // set info and categories
.subcommand( .subcommand(
"os", "os",
os::os_api::<C>().with_about("Commands related to OS assets and versions"), os::os_api::<C>().with_about("about.commands-os-assets-versions"),
) )
.subcommand( .subcommand(
"package", "package",
package::package_api::<C>().with_about("Commands to index, add, or get packages"), package::package_api::<C>().with_about("about.commands-index-add-get-packages"),
) )
.subcommand( .subcommand(
"admin", "admin",
admin::admin_api::<C>().with_about("Commands to add or list admins or signers"), admin::admin_api::<C>().with_about("about.commands-add-list-admins-signers"),
) )
.subcommand( .subcommand(
"db", "db",
db::db_api::<C>().with_about("Commands to interact with the db i.e. dump and apply"), db::db_api::<C>().with_about("about.commands-registry-db"),
) )
} }

View File

@@ -141,7 +141,7 @@ async fn add_asset(
.mutate(|s| { .mutate(|s| {
if s.commitment != commitment { if s.commitment != commitment {
Err(Error::new( Err(Error::new(
eyre!("commitment does not match"), eyre!("{}", t!("registry.os.asset.commitment-mismatch")),
ErrorKind::InvalidSignature, ErrorKind::InvalidSignature,
)) ))
} else { } else {
@@ -154,7 +154,7 @@ async fn add_asset(
})?; })?;
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await .await
@@ -208,7 +208,7 @@ pub async fn cli_add_asset(
Some("squashfs") => "squashfs", Some("squashfs") => "squashfs",
_ => { _ => {
return Err(Error::new( return Err(Error::new(
eyre!("Unknown extension"), eyre!("{}", t!("registry.os.asset.unknown-extension")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -232,7 +232,7 @@ pub async fn cli_add_asset(
let size = file let size = file
.size() .size()
.await .await
.ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?; .ok_or_else(|| Error::new(eyre!("{}", t!("registry.os.asset.failed-read-metadata")), ErrorKind::Filesystem))?;
let commitment = Blake3Commitment { let commitment = Blake3Commitment {
hash: Base64(*blake3.as_bytes()), hash: Base64(*blake3.as_bytes()),
size, size,
@@ -334,7 +334,7 @@ async fn remove_asset(
.remove(&platform)?; .remove(&platform)?;
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await .await

View File

@@ -34,7 +34,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"iso", "iso",
from_fn_async(cli_get_os_asset) from_fn_async(cli_get_os_asset)
.no_display() .no_display()
.with_about("Download iso"), .with_about("about.download-iso"),
) )
.subcommand( .subcommand(
"img", "img",
@@ -46,7 +46,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"img", "img",
from_fn_async(cli_get_os_asset) from_fn_async(cli_get_os_asset)
.no_display() .no_display()
.with_about("Download img"), .with_about("about.download-img"),
) )
.subcommand( .subcommand(
"squashfs", "squashfs",
@@ -58,7 +58,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"squashfs", "squashfs",
from_fn_async(cli_get_os_asset) from_fn_async(cli_get_os_asset)
.no_display() .no_display()
.with_about("Download squashfs"), .with_about("about.download-squashfs"),
) )
} }

View File

@@ -11,7 +11,7 @@ pub fn asset_api<C: Context>() -> ParentHandler<C> {
"add", "add",
from_fn_async(add::cli_add_asset) from_fn_async(add::cli_add_asset)
.no_display() .no_display()
.with_about("Add asset to registry"), .with_about("about.add-asset-registry"),
) )
.subcommand("remove", add::remove_api::<C>()) .subcommand("remove", add::remove_api::<C>())
.subcommand("sign", sign::sign_api::<C>()) .subcommand("sign", sign::sign_api::<C>())
@@ -19,10 +19,10 @@ pub fn asset_api<C: Context>() -> ParentHandler<C> {
"sign", "sign",
from_fn_async(sign::cli_sign_asset) from_fn_async(sign::cli_sign_asset)
.no_display() .no_display()
.with_about("Sign file and add to registry index"), .with_about("about.sign-file-add-registry"),
) )
.subcommand( .subcommand(
"get", "get",
get::get_api::<C>().with_about("Commands to download image, iso, or squashfs files"), get::get_api::<C>().with_about("about.commands-download-assets"),
) )
} }

View File

@@ -89,7 +89,7 @@ async fn sign_asset(
.contains(&guid) .contains(&guid)
{ {
return Err(Error::new( return Err(Error::new(
eyre!("signer {guid} is not authorized"), eyre!("{}", t!("registry.os.asset.signer-not-authorized", guid = guid)),
ErrorKind::Authorization, ErrorKind::Authorization,
)); ));
} }
@@ -163,7 +163,7 @@ pub async fn cli_sign_asset(
Some("squashfs") => "squashfs", Some("squashfs") => "squashfs",
_ => { _ => {
return Err(Error::new( return Err(Error::new(
eyre!("Unknown extension"), eyre!("{}", t!("registry.os.asset.unknown-extension")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -186,7 +186,7 @@ pub async fn cli_sign_asset(
let size = file let size = file
.size() .size()
.await .await
.ok_or_else(|| Error::new(eyre!("failed to read file metadata"), ErrorKind::Filesystem))?; .ok_or_else(|| Error::new(eyre!("{}", t!("registry.os.asset.failed-read-metadata")), ErrorKind::Filesystem))?;
let commitment = Blake3Commitment { let commitment = Blake3Commitment {
hash: Base64(*blake3.as_bytes()), hash: Base64(*blake3.as_bytes()),
size, size,

View File

@@ -17,16 +17,16 @@ pub fn os_api<C: Context>() -> ParentHandler<C> {
from_fn_async(index::get_os_index) from_fn_async(index::get_os_index)
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_display_serializable() .with_display_serializable()
.with_about("List index of OS versions") .with_about("about.list-os-versions-index")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"asset", "asset",
asset::asset_api::<C>().with_about("Commands to add, sign, or get registry assets"), asset::asset_api::<C>().with_about("about.commands-add-sign-get-assets"),
) )
.subcommand( .subcommand(
"version", "version",
version::version_api::<C>() version::version_api::<C>()
.with_about("Commands to add, remove, or list versions or version signers"), .with_about("about.commands-add-remove-list-versions"),
) )
} }

View File

@@ -27,7 +27,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.with_metadata("get_signer", Value::Bool(true)) .with_metadata("get_signer", Value::Bool(true))
.no_display() .no_display()
.with_about("Add OS version") .with_about("about.add-os-version")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -35,12 +35,12 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_version) from_fn_async(remove_version)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove OS version") .with_about("about.remove-os-version")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"signer", "signer",
signer::signer_api::<C>().with_about("Add, remove, and list version signers"), signer::signer_api::<C>().with_about("about.add-remove-list-version-signers"),
) )
.subcommand( .subcommand(
"get", "get",
@@ -51,7 +51,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
display_version_info(handle.params, result) display_version_info(handle.params, result)
}) })
.with_about("Get OS versions and related version info") .with_about("about.get-os-versions-info")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -21,7 +21,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_version_signer) from_fn_async(add_version_signer)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Add version signer") .with_about("about.add-version-signer")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -29,7 +29,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_version_signer) from_fn_async(remove_version_signer)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove version signer") .with_about("about.remove-version-signer")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -38,7 +38,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_display_serializable() .with_display_serializable()
.with_custom_display_fn(|handle, result| display_signers(handle.params, result)) .with_custom_display_fn(|handle, result| display_signers(handle.params, result))
.with_about("List version signers and related signer info") .with_about("about.list-version-signers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -95,7 +95,7 @@ pub async fn remove_version_signer(
.mutate(|s| Ok(s.remove(&signer)))? .mutate(|s| Ok(s.remove(&signer)))?
{ {
return Err(Error::new( return Err(Error::new(
eyre!("signer {signer} is not authorized to sign for v{version}"), eyre!("{}", t!("registry.os.version.signer-not-authorized", signer = signer, version = version)),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
} }

View File

@@ -56,7 +56,7 @@ pub async fn add_package(
let Some(([url], rest)) = urls.split_at_checked(1) else { let Some(([url], rest)) = urls.split_at_checked(1) else {
return Err(Error::new( return Err(Error::new(
eyre!("must specify at least 1 url"), eyre!("{}", t!("registry.package.add.must-specify-url")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
}; };
@@ -112,7 +112,7 @@ pub async fn add_package(
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.package.add.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await .await
@@ -226,7 +226,7 @@ pub async fn remove_package(
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let peek = ctx.db.peek().await; let peek = ctx.db.peek().await;
let signer = let signer =
signer.ok_or_else(|| Error::new(eyre!("missing signer"), ErrorKind::InvalidRequest))?; signer.ok_or_else(|| Error::new(eyre!("{}", t!("registry.package.missing-signer")), ErrorKind::InvalidRequest))?;
let signer_guid = peek.as_index().as_signers().get_signer(&signer)?; let signer_guid = peek.as_index().as_signers().get_signer(&signer)?;
let rev = ctx let rev = ctx
@@ -267,7 +267,7 @@ pub async fn remove_package(
} }
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.package.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await; .await;
@@ -342,7 +342,7 @@ pub async fn add_mirror(
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.package.add-mirror.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await .await
@@ -454,7 +454,7 @@ pub async fn remove_mirror(
) -> Result<(), Error> { ) -> Result<(), Error> {
let peek = ctx.db.peek().await; let peek = ctx.db.peek().await;
let signer = let signer =
signer.ok_or_else(|| Error::new(eyre!("missing signer"), ErrorKind::InvalidRequest))?; signer.ok_or_else(|| Error::new(eyre!("{}", t!("registry.package.missing-signer")), ErrorKind::InvalidRequest))?;
let signer_guid = peek.as_index().as_signers().get_signer(&signer)?; let signer_guid = peek.as_index().as_signers().get_signer(&signer)?;
ctx.db ctx.db
@@ -483,7 +483,7 @@ pub async fn remove_mirror(
.for_each(|(_, asset)| asset.urls.retain(|u| u != &url)); .for_each(|(_, asset)| asset.urls.retain(|u| u != &url));
if s.iter().any(|(_, asset)| asset.urls.is_empty()) { if s.iter().any(|(_, asset)| asset.urls.is_empty()) {
Err(Error::new( Err(Error::new(
eyre!("cannot remove last mirror from an s9pk"), eyre!("{}", t!("registry.package.cannot-remove-last-mirror")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)) ))
} else { } else {
@@ -493,7 +493,7 @@ pub async fn remove_mirror(
} }
Ok(()) Ok(())
} else { } else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization)) Err(Error::new(eyre!("{}", t!("registry.package.remove-mirror.unauthorized")), ErrorKind::Authorization))
} }
}) })
.await .await

View File

@@ -20,7 +20,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_category) from_fn_async(add_category)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Add a category to the registry") .with_about("about.add-category-registry")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -28,7 +28,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_category) from_fn_async(remove_category)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a category from the registry") .with_about("about.remove-category-registry")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -36,7 +36,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_package) from_fn_async(add_package)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Add a package to a category") .with_about("about.add-package-category")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -44,7 +44,7 @@ pub fn category_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_package) from_fn_async(remove_package)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a package from a category") .with_about("about.remove-package-category")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(

View File

@@ -441,8 +441,8 @@ pub async fn cli_download(
0 => { 0 => {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"Could not find a version of {id} that satisfies {}", "{}",
target_version.unwrap_or(VersionRange::Any) t!("registry.package.get.version-not-found", id = id, version = target_version.unwrap_or(VersionRange::Any))
), ),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
@@ -462,8 +462,8 @@ pub async fn cli_download(
0 => { 0 => {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"Could not find a version of {id} that satisfies {}", "{}",
target_version.unwrap_or(VersionRange::Any) t!("registry.package.get.version-not-found", id = id, version = target_version.unwrap_or(VersionRange::Any))
), ),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
@@ -551,7 +551,7 @@ pub async fn cli_download(
progress_tracker.complete(); progress_tracker.complete();
progress.await.unwrap(); progress.await.unwrap();
println!("Download Complete"); println!("{}", t!("registry.package.get.download-complete"));
Ok(()) Ok(())
} }

View File

@@ -17,7 +17,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
from_fn_async(index::get_package_index) from_fn_async(index::get_package_index)
.with_metadata("authenticated", Value::Bool(false)) .with_metadata("authenticated", Value::Bool(false))
.with_display_serializable() .with_display_serializable()
.with_about("List packages and categories") .with_about("about.list-packages-categories")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -30,7 +30,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
"add", "add",
from_fn_async(add::cli_add_package) from_fn_async(add::cli_add_package)
.no_display() .no_display()
.with_about("Add package to registry index"), .with_about("about.add-package-registry"),
) )
.subcommand( .subcommand(
"add-mirror", "add-mirror",
@@ -42,7 +42,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
"add-mirror", "add-mirror",
from_fn_async(add::cli_add_mirror) from_fn_async(add::cli_add_mirror)
.no_display() .no_display()
.with_about("Add a mirror for an s9pk"), .with_about("about.add-mirror-s9pk"),
) )
.subcommand( .subcommand(
"remove", "remove",
@@ -51,17 +51,17 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|args, changed| { .with_custom_display_fn(|args, changed| {
if !changed { if !changed {
tracing::warn!( tracing::warn!(
"{}@{}{} does not exist, so not removed", "{}",
args.params.id, t!("registry.package.remove-not-exist",
args.params.version, id = args.params.id,
args.params version = args.params.version,
.sighash sighash = args.params.sighash.map_or(String::new(), |h| format!("#{h}"))
.map_or(String::new(), |h| format!("#{h}")) )
); );
} }
Ok(()) Ok(())
}) })
.with_about("Remove package from registry index") .with_about("about.remove-package-registry")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -69,12 +69,12 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add::remove_mirror) from_fn_async(add::remove_mirror)
.with_metadata("get_signer", Value::Bool(true)) .with_metadata("get_signer", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a mirror from a package") .with_about("about.remove-mirror-package")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"signer", "signer",
signer::signer_api::<C>().with_about("Add, remove, and list package signers"), signer::signer_api::<C>().with_about("about.add-remove-list-package-signers"),
) )
.subcommand( .subcommand(
"get", "get",
@@ -85,18 +85,18 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
get::display_package_info(handle.params, result) get::display_package_info(handle.params, result)
}) })
.with_about("List installation candidate package(s)") .with_about("about.list-installation-candidates")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"download", "download",
from_fn_async_local(get::cli_download) from_fn_async_local(get::cli_download)
.no_display() .no_display()
.with_about("Download an s9pk"), .with_about("about.download-s9pk"),
) )
.subcommand( .subcommand(
"category", "category",
category::category_api::<C>() category::category_api::<C>()
.with_about("Update the categories for packages on the registry"), .with_about("about.update-categories-registry"),
) )
} }

View File

@@ -22,7 +22,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_package_signer) from_fn_async(add_package_signer)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Add package signer") .with_about("about.add-package-signer")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -30,7 +30,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_package_signer) from_fn_async(remove_package_signer)
.with_metadata("admin", Value::Bool(true)) .with_metadata("admin", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove package signer") .with_about("about.remove-package-signer")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -41,7 +41,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
display_package_signers(handle.params, result) display_package_signers(handle.params, result)
}) })
.with_about("List package signers and related signer info") .with_about("about.list-package-signers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -114,7 +114,7 @@ pub async fn remove_package_signer(
.is_some() .is_some()
{ {
return Err(Error::new( return Err(Error::new(
eyre!("signer {signer} is not authorized to sign for {id}"), eyre!("{}", t!("registry.package.signer.not-authorized", signer = signer, id = id)),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
} }

View File

@@ -90,7 +90,7 @@ impl AcceptSigners {
Ok(()) Ok(())
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("signer(s) not accepted"), eyre!("{}", t!("registry.signer.not-accepted")),
ErrorKind::InvalidSignature, ErrorKind::InvalidSignature,
)) ))
} }

View File

@@ -25,7 +25,7 @@ pub fn s9pk() -> ParentHandler<CliContext> {
"pack", "pack",
from_fn_async(super::v2::pack::pack) from_fn_async(super::v2::pack::pack)
.no_display() .no_display()
.with_about("Package s9pk input files into valid s9pk"), .with_about("about.package-s9pk-input-files-into-valid-s9pk"),
) )
.subcommand( .subcommand(
"list-ingredients", "list-ingredients",
@@ -45,21 +45,21 @@ pub fn s9pk() -> ParentHandler<CliContext> {
println!(); println!();
Ok(()) Ok(())
}) })
.with_about("List paths of package ingredients"), .with_about("about.list-paths-of-package-ingredients"),
) )
.subcommand( .subcommand(
"edit", "edit",
edit().with_about("Commands to add an image to an s9pk or edit the manifest"), edit().with_about("about.commands-add-image-or-edit-manifest"),
) )
.subcommand( .subcommand(
"inspect", "inspect",
inspect().with_about("Commands to display file paths, file contents, or manifest"), inspect().with_about("about.commands-display-file-paths-contents-manifest"),
) )
.subcommand( .subcommand(
"convert", "convert",
from_fn_async(convert) from_fn_async(convert)
.no_display() .no_display()
.with_about("Convert s9pk from v1 to v2"), .with_about("about.convert-s9pk-v1-to-v2"),
) )
} }
@@ -76,14 +76,14 @@ fn edit() -> ParentHandler<CliContext, S9pkPath> {
from_fn_async(add_image) from_fn_async(add_image)
.with_inherited(only_parent) .with_inherited(only_parent)
.no_display() .no_display()
.with_about("Add image to s9pk"), .with_about("about.add-image-to-s9pk"),
) )
.subcommand( .subcommand(
"manifest", "manifest",
from_fn_async(edit_manifest) from_fn_async(edit_manifest)
.with_inherited(only_parent) .with_inherited(only_parent)
.with_display_serializable() .with_display_serializable()
.with_about("Edit s9pk manifest"), .with_about("about.edit-s9pk-manifest"),
) )
} }
@@ -95,21 +95,21 @@ fn inspect() -> ParentHandler<CliContext, S9pkPath> {
from_fn_async(file_tree) from_fn_async(file_tree)
.with_inherited(only_parent) .with_inherited(only_parent)
.with_display_serializable() .with_display_serializable()
.with_about("Display list of paths"), .with_about("about.display-list-of-paths"),
) )
.subcommand( .subcommand(
"cat", "cat",
from_fn_async(cat) from_fn_async(cat)
.with_inherited(only_parent) .with_inherited(only_parent)
.no_display() .no_display()
.with_about("Display file contents"), .with_about("about.display-file-contents"),
) )
.subcommand( .subcommand(
"manifest", "manifest",
from_fn_async(inspect_manifest) from_fn_async(inspect_manifest)
.with_inherited(only_parent) .with_inherited(only_parent)
.with_display_serializable() .with_display_serializable()
.with_about("Display s9pk manifest"), .with_about("about.display-s9pk-manifest"),
) )
} }

View File

@@ -102,7 +102,7 @@ pub fn update_tasks(
} }
} }
None => { None => {
tracing::error!("action request exists in an invalid state {:?}", v.task); tracing::error!("{}", t!("service.action.action-request-invalid-state", task = format!("{:?}", v.task)));
} }
}, },
} }
@@ -151,7 +151,7 @@ impl Handler<RunAction> for ServiceActor {
.de()?; .de()?;
if matches!(&action.visibility, ActionVisibility::Disabled(_)) { if matches!(&action.visibility, ActionVisibility::Disabled(_)) {
return Err(Error::new( return Err(Error::new(
eyre!("action {action_id} is disabled"), eyre!("{}", t!("service.action.action-is-disabled", action_id = action_id)),
ErrorKind::Action, ErrorKind::Action,
)); ));
} }
@@ -162,7 +162,7 @@ impl Handler<RunAction> for ServiceActor {
_ => false, _ => false,
} { } {
return Err(Error::new( return Err(Error::new(
eyre!("service is not in allowed status for {action_id}"), eyre!("{}", t!("service.action.service-not-in-allowed-status", action_id = action_id)),
ErrorKind::Action, ErrorKind::Action,
)); ));
} }

View File

@@ -1,6 +1,7 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async}; use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
use rust_i18n::t;
use crate::action::{ActionInput, ActionResult, display_action_result}; use crate::action::{ActionInput, ActionResult, display_action_result};
use crate::db::model::package::{ use crate::db::model::package::{
@@ -175,7 +176,7 @@ async fn run_action(
if package_id != &context.seed.id { if package_id != &context.seed.id {
return Err(Error::new( return Err(Error::new(
eyre!("calling actions on other packages is unsupported at this time"), eyre!("{}", t!("service.effects.action.calling-actions-on-other-packages-unsupported")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
context context
@@ -220,7 +221,7 @@ async fn create_task(
TaskCondition::InputNotMatches => { TaskCondition::InputNotMatches => {
let Some(input) = task.input.as_ref() else { let Some(input) = task.input.as_ref() else {
return Err(Error::new( return Err(Error::new(
eyre!("input-not-matches trigger requires input to be specified"), eyre!("{}", t!("service.effects.action.input-not-matches-requires-input")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
}; };
@@ -238,9 +239,7 @@ async fn create_task(
else { else {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"action {} of {} has no input", "{}", t!("service.effects.action.action-has-no-input", action_id = task.action_id, package_id = task.package_id)
task.action_id,
task.package_id
), ),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));

View File

@@ -5,6 +5,7 @@ use std::str::FromStr;
use clap::builder::ValueParserFactory; use clap::builder::ValueParserFactory;
use exver::VersionRange; use exver::VersionRange;
use imbl_value::InternedString; use imbl_value::InternedString;
use rust_i18n::t;
use crate::db::model::package::{ use crate::db::model::package::{
CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference,
@@ -148,13 +149,13 @@ impl FromStr for DependencyRequirement {
.map(|id| id.parse().map_err(Error::from)) .map(|id| id.parse().map_err(Error::from))
.collect(), .collect(),
Some((kind, _)) => Err(Error::new( Some((kind, _)) => Err(Error::new(
eyre!("unknown dependency kind {kind}"), eyre!("{}", t!("service.effects.dependency.unknown-dependency-kind", kind = kind)),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)), )),
None => match rest { None => match rest {
"r" | "running" => Ok(BTreeSet::new()), "r" | "running" => Ok(BTreeSet::new()),
kind => Err(Error::new( kind => Err(Error::new(
eyre!("unknown dependency kind {kind}"), eyre!("{}", t!("service.effects.dependency.unknown-dependency-kind", kind = kind)),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)), )),
}, },

View File

@@ -28,6 +28,7 @@ use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
use ts_rs::TS; use ts_rs::TS;
use url::Url; use url::Url;
use crate::context::{CliContext, RpcContext}; use crate::context::{CliContext, RpcContext};
use crate::db::model::package::{ use crate::db::model::package::{
InstalledState, ManifestPreference, PackageState, PackageStateMatchModelRef, TaskSeverity, InstalledState, ManifestPreference, PackageState, PackageStateMatchModelRef, TaskSeverity,
@@ -172,7 +173,7 @@ impl ServiceRef {
} }
let service = Arc::try_unwrap(self.0).map_err(|_| { let service = Arc::try_unwrap(self.0).map_err(|_| {
Error::new( Error::new(
eyre!("ServiceActor held somewhere after actor shutdown"), eyre!("{}", t!("service.mod.service-actor-held-after-shutdown")),
ErrorKind::Unknown, ErrorKind::Unknown,
) )
})?; })?;
@@ -183,7 +184,7 @@ impl ServiceRef {
Arc::try_unwrap(service.seed) Arc::try_unwrap(service.seed)
.map_err(|_| { .map_err(|_| {
Error::new( Error::new(
eyre!("ServiceActorSeed held somewhere after actor shutdown"), eyre!("{}", t!("service.mod.service-actor-seed-held-after-shutdown")),
ErrorKind::Unknown, ErrorKind::Unknown,
) )
})? })?
@@ -375,7 +376,7 @@ impl Service {
{ {
Ok(PackageState::Installed(InstalledState { manifest })) Ok(PackageState::Installed(InstalledState { manifest }))
} else { } else {
Err(Error::new(eyre!("Race condition detected - package state changed during load"), ErrorKind::Database)) Err(Error::new(eyre!("{}", t!("service.mod.race-condition-detected")), ErrorKind::Database))
} }
}) })
} }
@@ -446,7 +447,7 @@ impl Service {
handle_installed(S9pk::open(s9pk_path, Some(id)).await?).await handle_installed(S9pk::open(s9pk_path, Some(id)).await?).await
} }
PackageStateMatchModelRef::Error(e) => Err(Error::new( PackageStateMatchModelRef::Error(e) => Err(Error::new(
eyre!("Failed to parse PackageDataEntry, found {e:?}"), eyre!("{}", t!("service.mod.failed-to-parse-package-data-entry", error = format!("{e:?}"))),
ErrorKind::Deserialization, ErrorKind::Deserialization,
)), )),
} }
@@ -552,7 +553,7 @@ impl Service {
true true
} else { } else {
tracing::warn!( tracing::warn!(
"Deleting task {id} because action no longer exists" "{}", t!("service.mod.deleting-task-action-no-longer-exists", id = id)
); );
false false
} }
@@ -789,7 +790,7 @@ pub async fn attach(
.join("\n"); .join("\n");
return Err(Error::new( return Err(Error::new(
eyre!( eyre!(
"no matching subcontainers are running for {id}; some possible choices are:\n{subcontainers}" "{}", t!("service.mod.no-matching-subcontainers", id = id, subcontainers = subcontainers)
), ),
ErrorKind::NotFound, ErrorKind::NotFound,
)); ));
@@ -828,7 +829,7 @@ pub async fn attach(
.map(format_subcontainer_pair) .map(format_subcontainer_pair)
.join("\n"); .join("\n");
return Err(Error::new( return Err(Error::new(
eyre!("multiple subcontainers found for {id}: \n{subcontainer_ids}"), eyre!("{}", t!("service.mod.multiple-subcontainers-found", id = id, subcontainer_ids = subcontainer_ids)),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -985,7 +986,7 @@ pub async fn attach(
"signal" => { "signal" => {
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
eyre!("invalid byte length for signal: {}", data.len()), eyre!("{}", t!("service.mod.invalid-byte-length-for-signal", length = data.len())),
ErrorKind::InvalidRequest ErrorKind::InvalidRequest
)); ));
} }
@@ -1118,14 +1119,14 @@ async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand
} }
} }
Err(Error::new( Err(Error::new(
eyre!("Could not parse /etc/passwd for shell: {}", contents), eyre!("{}", t!("service.mod.could-not-parse-etc-passwd", contents = contents)),
ErrorKind::Filesystem, ErrorKind::Filesystem,
)) ))
} }
.await .await
.map(RootCommand) .map(RootCommand)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
tracing::error!("Could not get the /etc/passwd: {e}"); tracing::error!("{}", t!("service.mod.could-not-get-etc-passwd", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
RootCommand("/bin/sh".to_string()) RootCommand("/bin/sh".to_string())
}) })
@@ -1287,7 +1288,7 @@ pub async fn cli_attach(
"exit" => { "exit" => {
if data.len() != 4 { if data.len() != 4 {
return Err(Error::new( return Err(Error::new(
eyre!("invalid byte length for exit code: {}", data.len()), eyre!("{}", t!("service.mod.invalid-byte-length-for-exit-code", length = data.len())),
ErrorKind::InvalidRequest ErrorKind::InvalidRequest
)); ));
} }

View File

@@ -318,7 +318,7 @@ impl PersistentContainer {
.get() .get()
.ok_or_else(|| { .ok_or_else(|| {
Error::new( Error::new(
eyre!("PersistentContainer has been destroyed"), eyre!("{}", t!("service.persistent-container.container-destroyed")),
ErrorKind::Incoherent, ErrorKind::Incoherent,
) )
})? })?
@@ -354,7 +354,7 @@ impl PersistentContainer {
.get() .get()
.ok_or_else(|| { .ok_or_else(|| {
Error::new( Error::new(
eyre!("PersistentContainer has been destroyed"), eyre!("{}", t!("service.persistent-container.container-destroyed")),
ErrorKind::Incoherent, ErrorKind::Incoherent,
) )
})? })?
@@ -364,7 +364,7 @@ impl PersistentContainer {
let handle = NonDetachingJoinHandle::from(tokio::spawn(async move { let handle = NonDetachingJoinHandle::from(tokio::spawn(async move {
let chown_status = async { let chown_status = async {
let res = server.run_unix(&path, |err| { let res = server.run_unix(&path, |err| {
tracing::error!("error on unix socket {}: {err}", path.display()) tracing::error!("{}", t!("service.persistent-container.error-on-unix-socket", path = path.display(), error = err))
})?; })?;
Command::new("chown") Command::new("chown")
.arg("100000:100000") .arg("100000:100000")
@@ -386,7 +386,7 @@ impl PersistentContainer {
})); }));
let shutdown = recv.await.map_err(|_| { let shutdown = recv.await.map_err(|_| {
Error::new( Error::new(
eyre!("unix socket server thread panicked"), eyre!("{}", t!("service.persistent-container.unix-socket-server-panicked")),
ErrorKind::Unknown, ErrorKind::Unknown,
) )
})??; })??;
@@ -396,7 +396,7 @@ impl PersistentContainer {
.is_some() .is_some()
{ {
return Err(Error::new( return Err(Error::new(
eyre!("PersistentContainer already initialized"), eyre!("{}", t!("service.persistent-container.already-initialized")),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -473,7 +473,7 @@ impl PersistentContainer {
if let Some(destroy) = self.destroy(uninit) { if let Some(destroy) = self.destroy(uninit) {
destroy.await?; destroy.await?;
} }
tracing::info!("Service for {} exited", self.s9pk.as_manifest().id); tracing::info!("{}", t!("service.persistent-container.service-exited", id = self.s9pk.as_manifest().id));
Ok(()) Ok(())
} }

View File

@@ -47,9 +47,9 @@ impl Actor for ServiceActor {
} }
.await .await
{ {
tracing::error!("error synchronizing state of service: {e}"); tracing::error!("{}", t!("service.service-actor.error-synchronizing-state", error = e));
tracing::debug!("{e:?}"); tracing::debug!("{e:?}");
tracing::error!("Retrying in {}s...", SYNC_RETRY_COOLDOWN_SECONDS); tracing::error!("{}", t!("service.service-actor.retrying-in-seconds", seconds = SYNC_RETRY_COOLDOWN_SECONDS));
tokio::time::timeout( tokio::time::timeout(
Duration::from_secs(SYNC_RETRY_COOLDOWN_SECONDS), Duration::from_secs(SYNC_RETRY_COOLDOWN_SECONDS),
async { async {

View File

@@ -25,7 +25,7 @@ impl ServiceActorSeed {
fut.await.map_err(Error::from) fut.await.map_err(Error::from)
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("No backup to resume"), eyre!("{}", t!("service.transition.backup.no-backup-to-resume")),
ErrorKind::Cancelled, ErrorKind::Cancelled,
)) ))
}; };

View File

@@ -62,7 +62,7 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, soft: bool) -> Result<(),
| PackageState::Removing(InstalledState { manifest }) => manifest, | PackageState::Removing(InstalledState { manifest }) => manifest,
s => { s => {
return Err(Error::new( return Err(Error::new(
eyre!("Invalid package state for cleanup: {s:?}"), eyre!("{}", t!("service.uninstall.invalid-package-state-for-cleanup", state = format!("{s:?}"))),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }

View File

@@ -154,7 +154,7 @@ pub async fn attach(
let setup_ctx = ctx.clone(); let setup_ctx = ctx.clone();
ctx.run_setup(move || async move { ctx.run_setup(move || async move {
let progress = &setup_ctx.progress; let progress = &setup_ctx.progress;
let mut disk_phase = progress.add_phase("Opening data drive".into(), Some(10)); let mut disk_phase = progress.add_phase(t!("setup.opening-data-drive").into(), Some(10));
let init_phases = InitPhases::new(&progress); let init_phases = InitPhases::new(&progress);
let rpc_ctx_phases = InitRpcContextPhases::new(&progress); let rpc_ctx_phases = InitRpcContextPhases::new(&progress);
@@ -163,7 +163,7 @@ pub async fn attach(
a @ Some(_) => a, a @ Some(_) => a,
None => { None => {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("Couldn't decode password"), color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-password")),
crate::ErrorKind::Unknown, crate::ErrorKind::Unknown,
)); ));
} }
@@ -192,9 +192,7 @@ pub async fn attach(
if requires_reboot.0 { if requires_reboot.0 {
crate::disk::main::export(&*disk_guid, DATA_DIR).await?; crate::disk::main::export(&*disk_guid, DATA_DIR).await?;
return Err(Error::new( return Err(Error::new(
eyre!( eyre!("{}", t!("setup.disk-errors-corrected-restart-required")),
"Errors were corrected with your disk, but the server must be restarted in order to proceed"
),
ErrorKind::DiskManagement, ErrorKind::DiskManagement,
)); ));
} }
@@ -313,7 +311,7 @@ pub async fn verify_cifs(
guard.unmount().await?; guard.unmount().await?;
if start_os.is_empty() { if start_os.is_empty() {
return Err(Error::new( return Err(Error::new(
eyre!("No Backup Found"), eyre!("{}", t!("setup.no-backup-found")),
crate::ErrorKind::NotFound, crate::ErrorKind::NotFound,
)); ));
} }
@@ -382,7 +380,7 @@ pub async fn execute(
Some(a) => a, Some(a) => a,
None => { None => {
return Err(Error::new( return Err(Error::new(
color_eyre::eyre::eyre!("Couldn't decode startOsPassword"), color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-startos-password")),
crate::ErrorKind::Unknown, crate::ErrorKind::Unknown,
)); ));
} }
@@ -396,7 +394,7 @@ pub async fn execute(
target, target,
password: password.decrypt(&ctx).ok_or_else(|| { password: password.decrypt(&ctx).ok_or_else(|| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Couldn't decode recoveryPassword"), color_eyre::eyre::eyre!("{}", t!("setup.couldnt-decode-recovery-password")),
crate::ErrorKind::Unknown, crate::ErrorKind::Unknown,
) )
})?, })?,
@@ -439,7 +437,7 @@ pub async fn complete(ctx: SetupContext) -> Result<SetupResult, Error> {
} }
Some(Err(e)) => Err(e.clone_output()), Some(Err(e)) => Err(e.clone_output()),
None => Err(Error::new( None => Err(Error::new(
eyre!("setup.execute has not completed successfully"), eyre!("{}", t!("setup.execute-not-completed")),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)), )),
} }
@@ -471,13 +469,13 @@ pub async fn execute_inner(
kiosk: Option<bool>, kiosk: Option<bool>,
) -> Result<(SetupResult, RpcContext), Error> { ) -> Result<(SetupResult, RpcContext), Error> {
let progress = &ctx.progress; let progress = &ctx.progress;
let mut disk_phase = progress.add_phase("Formatting data drive".into(), Some(10)); let mut disk_phase = progress.add_phase(t!("setup.formatting-data-drive").into(), Some(10));
let restore_phase = match recovery_source.as_ref() { let restore_phase = match recovery_source.as_ref() {
Some(RecoverySource::Backup { .. }) => { Some(RecoverySource::Backup { .. }) => {
Some(progress.add_phase("Restoring backup".into(), Some(100))) Some(progress.add_phase(t!("setup.restoring-backup").into(), Some(100)))
} }
Some(RecoverySource::Migrate { .. }) => { Some(RecoverySource::Migrate { .. }) => {
Some(progress.add_phase("Transferring data".into(), Some(100))) Some(progress.add_phase(t!("setup.transferring-data").into(), Some(100)))
} }
None => None, None => None,
}; };

View File

@@ -1,3 +1,4 @@
use crate::PLATFORM; use crate::PLATFORM;
use crate::context::RpcContext; use crate::context::RpcContext;
use crate::disk::main::export; use crate::disk::main::export;
@@ -17,9 +18,9 @@ impl Shutdown {
use std::process::Command; use std::process::Command;
if self.restart { if self.restart {
tracing::info!("Beginning server restart"); tracing::info!("{}", t!("shutdown.beginning-restart"));
} else { } else {
tracing::info!("Beginning server shutdown"); tracing::info!("{}", t!("shutdown.beginning-shutdown"));
} }
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
@@ -35,18 +36,18 @@ impl Shutdown {
.invoke(crate::ErrorKind::Journald) .invoke(crate::ErrorKind::Journald)
.await .await
{ {
tracing::error!("Error Stopping Journald: {}", e); tracing::error!("{}", t!("shutdown.error-stopping-journald", error = e.to_string()));
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
if let Some(guid) = &self.disk_guid { if let Some(guid) = &self.disk_guid {
if let Err(e) = export(guid, crate::DATA_DIR).await { if let Err(e) = export(guid, crate::DATA_DIR).await {
tracing::error!("Error Exporting Volume Group: {}", e); tracing::error!("{}", t!("shutdown.error-exporting-volume-group", error = e.to_string()));
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
if &*PLATFORM != "raspberrypi" || self.restart { if &*PLATFORM != "raspberrypi" || self.restart {
if let Err(e) = SHUTDOWN.play().await { if let Err(e) = SHUTDOWN.play().await {
tracing::error!("Error Playing Shutdown Song: {}", e); tracing::error!("{}", t!("shutdown.error-playing-shutdown-song", error = e.to_string()));
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }

View File

@@ -92,14 +92,14 @@ pub fn ssh<C: Context>() -> ParentHandler<C> {
"add", "add",
from_fn_async(add) from_fn_async(add)
.no_display() .no_display()
.with_about("Add ssh key") .with_about("about.add-ssh-key")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove) from_fn_async(remove)
.no_display() .no_display()
.with_about("Remove ssh key") .with_about("about.remove-ssh-key")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -109,7 +109,7 @@ pub fn ssh<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
display_all_ssh_keys(handle.params, result) display_all_ssh_keys(handle.params, result)
}) })
.with_about("List ssh keys") .with_about("about.list-ssh-keys")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -168,7 +168,10 @@ pub async fn remove(
if keys_ref.remove(&fingerprint)?.is_some() { if keys_ref.remove(&fingerprint)?.is_some() {
keys_ref.de() keys_ref.de()
} else { } else {
Err(Error::new(eyre!("SSH Key Not Found"), ErrorKind::NotFound)) Err(Error::new(
eyre!("{}", t!("ssh.key-not-found")),
ErrorKind::NotFound,
))
} }
}) })
.await .await

View File

@@ -34,7 +34,7 @@ pub fn experimental<C: Context>() -> ParentHandler<C> {
"zram", "zram",
from_fn_async(zram) from_fn_async(zram)
.no_display() .no_display()
.with_about("Enable zram") .with_about("about.enable-zram")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -44,7 +44,7 @@ pub fn experimental<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| { .with_custom_display_fn(|handle, result| {
display_governor_info(handle.params, result) display_governor_info(handle.params, result)
}) })
.with_about("Show current and available CPU governors") .with_about("about.show-cpu-governors")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -159,7 +159,10 @@ pub async fn governor(
if let Some(set) = set { if let Some(set) = set {
if !available.contains(&set) { if !available.contains(&set) {
return Err(Error::new( return Err(Error::new(
eyre!("Governor {set} not available"), eyre!(
"{}",
t!("system.governor-not-available", governor = set.to_string())
),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -295,7 +298,7 @@ pub fn kiosk<C: Context>() -> ParentHandler<C> {
enable_kiosk().await enable_kiosk().await
}) })
.no_display() .no_display()
.with_about("Enable kiosk mode") .with_about("about.enable-kiosk-mode")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -313,7 +316,7 @@ pub fn kiosk<C: Context>() -> ParentHandler<C> {
disable_kiosk().await disable_kiosk().await
}) })
.no_display() .no_display()
.with_about("Disable kiosk mode") .with_about("about.disable-kiosk-mode")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -553,7 +556,13 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
let init_temp = match get_temp().await { let init_temp = match get_temp().await {
Ok(a) => Some(a), Ok(a) => Some(a),
Err(e) => { Err(e) => {
tracing::error!("Could not get initial temperature: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-initial-temperature",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
None None
} }
@@ -570,12 +579,24 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break; break;
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get initial cpu info: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-initial-cpu-info",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
}, },
Err(e) => { Err(e) => {
tracing::error!("Could not get initial proc stat: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-initial-proc-stat",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -590,7 +611,13 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break; break;
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get initial mem info: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-initial-mem-info",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -605,7 +632,13 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break; break;
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get initial disk info: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-initial-disk-info",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -650,7 +683,13 @@ async fn launch_temp_task(
}); });
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get new temperature: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-new-temperature",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -673,7 +712,13 @@ async fn launch_cpu_task(
cache.send_modify(|c| c.as_mut().unwrap().cpu = info); cache.send_modify(|c| c.as_mut().unwrap().cpu = info);
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get new CPU Metrics: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-new-cpu-metrics",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -692,7 +737,13 @@ async fn launch_mem_task(cache: &Watch<Option<Metrics>>, mut shutdown: Receiver<
cache.send_modify(|c| c.as_mut().unwrap().memory = a); cache.send_modify(|c| c.as_mut().unwrap().memory = a);
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get new Memory Metrics: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-new-memory-metrics",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -721,7 +772,13 @@ async fn launch_disk_task(
}); });
} }
Err(e) => { Err(e) => {
tracing::error!("Could not get new Disk Metrics: {}", e); tracing::error!(
"{}",
t!(
"system.could-not-get-new-disk-metrics",
error = e.to_string()
)
);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
@@ -757,7 +814,12 @@ async fn get_temp() -> Result<Celsius, Error> {
} }
}) })
.reduce(f64::max) .reduce(f64::max)
.ok_or_else(|| Error::new(eyre!("No temperatures available"), ErrorKind::Filesystem))?; .ok_or_else(|| {
Error::new(
eyre!("{}", t!("system.no-temperatures-available")),
ErrorKind::Filesystem,
)
})?;
Ok(Celsius(temp)) Ok(Celsius(temp))
} }
@@ -803,7 +865,10 @@ async fn get_proc_stat() -> Result<ProcStat, Error> {
.map(|s| { .map(|s| {
s.parse::<u64>().map_err(|e| { s.parse::<u64>().map_err(|e| {
Error::new( Error::new(
color_eyre::eyre::eyre!("Invalid /proc/stat column value: {}", e), color_eyre::eyre::eyre!(
"{}",
t!("system.invalid-proc-stat-column", error = e.to_string())
),
ErrorKind::ParseSysInfo, ErrorKind::ParseSysInfo,
) )
}) })
@@ -813,8 +878,8 @@ async fn get_proc_stat() -> Result<ProcStat, Error> {
if stats.len() < 10 { if stats.len() < 10 {
Err(Error::new( Err(Error::new(
eyre!( eyre!(
"Columns missing from /proc/stat. Need 10, found {}", "{}",
stats.len() t!("system.columns-missing-from-proc-stat", count = stats.len())
), ),
ErrorKind::ParseSysInfo, ErrorKind::ParseSysInfo,
)) ))
@@ -872,16 +937,22 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
zram_free: None, zram_free: None,
}; };
fn get_num_kb(l: &str) -> Result<u64, Error> { fn get_num_kb(l: &str) -> Result<u64, Error> {
let e = Error::new( let line = l.to_string();
color_eyre::eyre::eyre!("Invalid meminfo line: {}", l), let e = || {
ErrorKind::ParseSysInfo, Error::new(
); color_eyre::eyre::eyre!(
"{}",
t!("system.invalid-meminfo-line", line = line.clone())
),
ErrorKind::ParseSysInfo,
)
};
match l.split_whitespace().skip(1).next() { match l.split_whitespace().skip(1).next() {
Some(x) => match x.parse() { Some(x) => match x.parse() {
Ok(y) => Ok(y), Ok(y) => Ok(y),
Err(_) => Err(e), Err(_) => Err(e()),
}, },
None => Err(e), None => Err(e()),
} }
} }
for entry in contents.lines() { for entry in contents.lines() {
@@ -900,10 +971,19 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
} }
} }
fn ensure_present(a: Option<u64>, field: &str) -> Result<u64, Error> { fn ensure_present(a: Option<u64>, field: &str) -> Result<u64, Error> {
a.ok_or(Error::new( let field_str = field.to_string();
color_eyre::eyre::eyre!("{} missing from /proc/meminfo", field), a.ok_or_else(|| {
ErrorKind::ParseSysInfo, Error::new(
)) color_eyre::eyre::eyre!(
"{}",
t!(
"system.field-missing-from-meminfo",
field = field_str.clone()
)
),
ErrorKind::ParseSysInfo,
)
})
} }
let mem_total = ensure_present(mem_info.mem_total, "MemTotal")?; let mem_total = ensure_present(mem_info.mem_total, "MemTotal")?;
let mem_free = ensure_present(mem_info.mem_free, "MemFree")?; let mem_free = ensure_present(mem_info.mem_free, "MemFree")?;
@@ -1136,7 +1216,7 @@ pub async fn set_language(
.await?; .await?;
write_file_atomic( write_file_atomic(
"/media/startos/config/overlay/etc/default/locale", "/media/startos/config/overlay/etc/default/locale",
format!("{language}.UTF-8\n").as_bytes(), format!("LANG={language}.UTF-8\n").as_bytes(),
) )
.await?; .await?;
ctx.db ctx.db

View File

@@ -20,19 +20,19 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"db", "db",
super::db::db_api::<C>() super::db::db_api::<C>()
.with_about("Commands to interact with the db i.e. dump and apply"), .with_about("about.commands-interact-with-db-dump-apply"),
) )
.subcommand( .subcommand(
"auth", "auth",
super::auth::auth_api::<C>().with_about("Add or remove authorized clients"), super::auth::auth_api::<C>().with_about("about.add-or-remove-authorized-clients"),
) )
.subcommand( .subcommand(
"subnet", "subnet",
subnet_api::<C>().with_about("Add, remove, or modify subnets"), subnet_api::<C>().with_about("about.add-remove-or-modify-subnets"),
) )
.subcommand( .subcommand(
"device", "device",
device_api::<C>().with_about("Add, remove, or list devices in subnets"), device_api::<C>().with_about("about.add-remove-or-list-devices-in-subnets"),
) )
.subcommand( .subcommand(
"port-forward", "port-forward",
@@ -42,7 +42,7 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_forward) from_fn_async(add_forward)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Add a new port forward") .with_about("about.add-new-port-forward")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -50,7 +50,7 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_forward) from_fn_async(remove_forward)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a port forward") .with_about("about.remove-port-forward")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
), ),
) )
@@ -70,7 +70,7 @@ pub fn subnet_api<C: Context>() -> ParentHandler<C, SubnetParams> {
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|a, _| a) .with_inherited(|a, _| a)
.no_display() .no_display()
.with_about("Add a new subnet") .with_about("about.add-new-subnet")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -79,7 +79,7 @@ pub fn subnet_api<C: Context>() -> ParentHandler<C, SubnetParams> {
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.with_inherited(|a, _| a) .with_inherited(|a, _| a)
.no_display() .no_display()
.with_about("Remove a subnet") .with_about("about.remove-subnet")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }
@@ -91,7 +91,7 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_device) from_fn_async(add_device)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Add a device to a subnet") .with_about("about.add-device-to-subnet")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -99,7 +99,7 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_device) from_fn_async(remove_device)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove a device from a subnet") .with_about("about.remove-device-from-subnet")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -123,13 +123,13 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
Ok(()) Ok(())
}) })
.with_about("List devices in a subnet") .with_about("about.list-devices-in-subnet")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"show-config", "show-config",
from_fn_async(show_config) from_fn_async(show_config)
.with_about("Show the WireGuard configuration for a device") .with_about("about.show-wireguard-configuration-for-device")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -129,13 +129,13 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
.subcommand( .subcommand(
"set-password", "set-password",
from_fn_async(set_password_cli) from_fn_async(set_password_cli)
.with_about("Set user interface password") .with_about("about.set-user-interface-password")
.no_display(), .no_display(),
) )
.subcommand( .subcommand(
"reset-password", "reset-password",
from_fn_async(reset_password) from_fn_async(reset_password)
.with_about("Reset user interface password") .with_about("about.reset-user-interface-password")
.no_display(), .no_display(),
) )
.subcommand( .subcommand(
@@ -146,7 +146,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_key) from_fn_async(add_key)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Add a new authorized key") .with_about("about.add-new-authorized-key")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -154,7 +154,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_key) from_fn_async(remove_key)
.with_metadata("sync_db", Value::Bool(true)) .with_metadata("sync_db", Value::Bool(true))
.no_display() .no_display()
.with_about("Remove an authorized key") .with_about("about.remove-authorized-key")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -175,7 +175,7 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
table.print_tty(false)?; table.print_tty(false)?;
Ok(()) Ok(())
}) })
.with_about("List authorized keys") .with_about("about.list-authorized-keys")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
), ),
) )

View File

@@ -89,7 +89,7 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
"dump", "dump",
from_fn_async(cli_dump) from_fn_async(cli_dump)
.with_display_serializable() .with_display_serializable()
.with_about("Filter/query db to display tables and records"), .with_about("about.filter-query-db-display-tables-records"),
) )
.subcommand( .subcommand(
"dump", "dump",
@@ -107,7 +107,7 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
"apply", "apply",
from_fn_async(cli_apply) from_fn_async(cli_apply)
.no_display() .no_display()
.with_about("Update a db record"), .with_about("about.update-db-record"),
) )
.subcommand( .subcommand(
"apply", "apply",

View File

@@ -98,27 +98,27 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
"init", "init",
from_fn_async_local(init_web) from_fn_async_local(init_web)
.no_display() .no_display()
.with_about("Initialize the webserver"), .with_about("about.initialize-webserver"),
) )
.subcommand( .subcommand(
"set-listen", "set-listen",
from_fn_async(set_listen) from_fn_async(set_listen)
.no_display() .no_display()
.with_about("Set the listen address for the webserver") .with_about("about.set-listen-address-for-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"get-listen", "get-listen",
from_fn_async(get_listen) from_fn_async(get_listen)
.with_display_serializable() .with_display_serializable()
.with_about("Get the listen address for the webserver") .with_about("about.get-listen-address-for-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"get-available-ips", "get-available-ips",
from_fn_async(get_available_ips) from_fn_async(get_available_ips)
.with_display_serializable() .with_display_serializable()
.with_about("Get available IP addresses to bind to") .with_about("about.get-available-ip-addresses-to-bind")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -129,12 +129,12 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
"import-certificate", "import-certificate",
from_fn_async_local(import_certificate_cli) from_fn_async_local(import_certificate_cli)
.no_display() .no_display()
.with_about("Import a certificate to use for the webserver"), .with_about("about.import-certificate-for-webserver"),
) )
.subcommand( .subcommand(
"generate-certificate", "generate-certificate",
from_fn_async(generate_certificate) from_fn_async(generate_certificate)
.with_about("Generate a certificate to use for the webserver") .with_about("about.generate-certificate-for-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
@@ -150,13 +150,13 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
} }
Ok(()) Ok(())
}) })
.with_about("Get the certificate for the webserver") .with_about("about.get-certificate-for-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"enable", "enable",
from_fn_async(enable_web) from_fn_async(enable_web)
.with_about("Enable the webserver") .with_about("about.enable-webserver")
.no_display() .no_display()
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
@@ -164,14 +164,14 @@ pub fn web_api<C: Context>() -> ParentHandler<C> {
"disable", "disable",
from_fn_async(disable_web) from_fn_async(disable_web)
.no_display() .no_display()
.with_about("Disable the webserver") .with_about("about.disable-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand( .subcommand(
"reset", "reset",
from_fn_async(reset_web) from_fn_async(reset_web)
.no_display() .no_display()
.with_about("Reset the webserver") .with_about("about.reset-webserver")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
} }

View File

@@ -41,6 +41,7 @@ impl WgServer {
Command::new("wg-quick") Command::new("wg-quick")
.arg("down") .arg("down")
.arg(WIREGUARD_INTERFACE_NAME) .arg(WIREGUARD_INTERFACE_NAME)
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::Network) .invoke(ErrorKind::Network)
.await .await
.or_else(|e| { .or_else(|e| {

View File

@@ -82,9 +82,7 @@ pub async fn update_system(
.de()? .de()?
{ {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!("{}", t!("update.already-updated-restart-required")),
"Server was already updated. Please restart your device before attempting to update again."
),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)); ));
} }
@@ -143,7 +141,7 @@ pub async fn update_system(
} }
.await .await
{ {
tracing::error!("Error returning progress of update: {e}"); tracing::error!("{}", t!("update.error-returning-progress", error = e.to_string()));
tracing::debug!("{e:?}") tracing::debug!("{e:?}")
} }
}, },
@@ -176,11 +174,11 @@ pub async fn cli_update_system(
.await?, .await?,
)?; )?;
match res.target { match res.target {
None => println!("No updates available"), None => println!("{}", t!("update.no-updates-available")),
Some(v) => { Some(v) => {
if let Some(progress) = res.progress { if let Some(progress) = res.progress {
let mut ws = context.ws_continuation(progress).await?; let mut ws = context.ws_continuation(progress).await?;
let mut progress = PhasedProgressBar::new(&format!("Updating to v{v}...")); let mut progress = PhasedProgressBar::new(&t!("update.updating-to-version", version = v.to_string()));
let mut prev = None; let mut prev = None;
while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? { while let Some(msg) = ws.try_next().await.with_kind(ErrorKind::Network)? {
if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg { if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg {
@@ -201,9 +199,9 @@ pub async fn cli_update_system(
prev.overall.set_complete(); prev.overall.set_complete();
progress.update(&prev); progress.update(&prev);
} }
println!("Update complete. Restart your server to apply the update.") println!("{}", t!("update.complete-restart-to-apply"))
} else { } else {
println!("Updating to v{v}...") println!("{}", t!("update.updating-to-version", version = v.to_string()))
} }
} }
} }
@@ -279,7 +277,7 @@ async fn maybe_do_update(
let mut status = peeked.as_public().as_server_info().as_status_info().de()?; let mut status = peeked.as_public().as_server_info().as_status_info().de()?;
if status.update_progress.is_some() { if status.update_progress.is_some() {
return Err(Error::new( return Err(Error::new(
eyre!("Server is already updating!"), eyre!("{}", t!("update.already-updating")),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
} }
@@ -296,9 +294,7 @@ async fn maybe_do_update(
if status.updated { if status.updated {
return Err(Error::new( return Err(Error::new(
eyre!( eyre!("{}", t!("update.already-updated-restart-required")),
"Server was already updated. Please restart your device before attempting to update again."
),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
} }
@@ -343,7 +339,7 @@ async fn maybe_do_update(
CIRCLE_OF_5THS_SHORT.play().await.log_err(); CIRCLE_OF_5THS_SHORT.play().await.log_err();
} }
Err(e) => { Err(e) => {
let err_string = format!("Update was not successful because of {}", e); let err_string = t!("update.not-successful", error = e.to_string()).to_string();
ctx.db ctx.db
.mutate(|db| { .mutate(|db| {
db.as_public_mut() db.as_public_mut()
@@ -355,7 +351,7 @@ async fn maybe_do_update(
db, db,
None, None,
NotificationLevel::Error, NotificationLevel::Error,
"StartOS Update Failed".to_owned(), t!("update.failed-title").to_string(),
err_string, err_string,
(), (),
) )

View File

@@ -67,7 +67,7 @@ pub async fn get_available_governors() -> Result<BTreeSet<Governor>, Error> {
for_cpu for_cpu
.entry(current_cpu.ok_or_else(|| { .entry(current_cpu.ok_or_else(|| {
Error::new( Error::new(
eyre!("governors listed before cpu"), eyre!("{}", t!("util.cpupower.governors-listed-before-cpu")),
ErrorKind::ParseSysInfo, ErrorKind::ParseSysInfo,
) )
})?) })?)
@@ -95,6 +95,7 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
let Some(raw) = Command::new("cpupower") let Some(raw) = Command::new("cpupower")
.arg("frequency-info") .arg("frequency-info")
.arg("-p") .arg("-p")
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::CpuSettings) .invoke(ErrorKind::CpuSettings)
.await .await
.and_then(|s| Ok(Some(String::from_utf8(s)?))) .and_then(|s| Ok(Some(String::from_utf8(s)?)))
@@ -122,7 +123,10 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
} }
} }
Err(Error::new( Err(Error::new(
eyre!("Failed to parse cpupower output:\n{raw}"), eyre!(
"{}",
t!("util.cpupower.failed-to-parse-output", output = raw)
),
ErrorKind::ParseSysInfo, ErrorKind::ParseSysInfo,
)) ))
} }

View File

@@ -666,6 +666,27 @@ pub fn new_guid() -> InternedString {
)) ))
} }
/// A utility for lazily computing a Display value. This is useful for i18n
/// where you want to defer the translation until the value is actually displayed,
/// avoiding allocations in the common case where the message is not rendered.
pub struct LazyDisplay<F>(F);
impl<F: Fn() -> D, D: fmt::Display> LazyDisplay<F> {
pub fn new(f: F) -> Self {
LazyDisplay(f)
}
}
impl<F: Fn() -> D, D: fmt::Display> fmt::Display for LazyDisplay<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", (self.0)())
}
}
/// Creates a lazily-evaluated Display implementation.
/// The closure is only called when the value is actually displayed.
pub fn lazy_display<F: Fn() -> D, D: fmt::Display>(f: F) -> LazyDisplay<F> {
LazyDisplay::new(f)
}
#[derive(Debug, Clone, TS)] #[derive(Debug, Clone, TS)]
#[ts(type = "string")] #[ts(type = "string")]
pub enum PathOrUrl { pub enum PathOrUrl {

View File

@@ -97,7 +97,7 @@ impl WebSocket {
if self.ping_state.is_some() { if self.ping_state.is_some() {
self.fused = true; self.fused = true;
break Poll::Ready(Some(Err(axum::Error::new(eyre!( break Poll::Ready(Some(Err(axum::Error::new(eyre!(
"Timeout: WebSocket did not respond to ping within {PING_TIMEOUT:?}" "{}", t!("util.net.websocket-ping-timeout", timeout = format!("{PING_TIMEOUT:?}"))
))))); )))));
} }
self.ping_state = Some((false, rand::random())); self.ping_state = Some((false, rand::random()));

View File

@@ -17,7 +17,7 @@ use crate::util::{Apply, PathOrUrl};
pub fn util<C: Context>() -> ParentHandler<C> { pub fn util<C: Context>() -> ParentHandler<C> {
ParentHandler::new().subcommand( ParentHandler::new().subcommand(
"b3sum", "b3sum",
from_fn_async(b3sum).with_about("Calculate blake3 hash for a file"), from_fn_async(b3sum).with_about("about.calculate-blake3-hash-for-file"),
) )
} }
@@ -57,7 +57,7 @@ pub async fn b3sum(
.await .await
} else { } else {
Err(Error::new( Err(Error::new(
eyre!("unknown scheme: {}", url.scheme()), eyre!("{}", t!("util.rpc.unknown-scheme", scheme = url.scheme())),
ErrorKind::InvalidRequest, ErrorKind::InvalidRequest,
)) ))
} }

View File

@@ -652,7 +652,7 @@ impl std::str::FromStr for Duration {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let units_idx = s.find(|c: char| c.is_alphabetic()).ok_or_else(|| { let units_idx = s.find(|c: char| c.is_alphabetic()).ok_or_else(|| {
Error::new( Error::new(
eyre!("Must specify units for duration"), eyre!("{}", t!("util.serde.must-specify-units-for-duration")),
crate::ErrorKind::Deserialization, crate::ErrorKind::Deserialization,
) )
})?; })?;
@@ -691,7 +691,7 @@ impl std::str::FromStr for Duration {
} }
_ => { _ => {
return Err(Error::new( return Err(Error::new(
eyre!("Invalid units for duration"), eyre!("{}", t!("util.serde.invalid-units-for-duration")),
crate::ErrorKind::Deserialization, crate::ErrorKind::Deserialization,
)); ));
} }
@@ -1050,7 +1050,7 @@ impl<T: TryFrom<Vec<u8>>> FromStr for Base64<T> {
.map(Self) .map(Self)
.map_err(|_| { .map_err(|_| {
Error::new( Error::new(
eyre!("failed to create from buffer"), eyre!("{}", t!("util.serde.failed-to-create-from-buffer")),
ErrorKind::Deserialization, ErrorKind::Deserialization,
) )
}) })
@@ -1151,7 +1151,7 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
let Some(expr) = expr else { let Some(expr) = expr else {
return Err(Error::new( return Err(Error::new(
eyre!("Failed to parse expression: {:?}", errs), eyre!("{}", t!("util.serde.failed-to-parse-expression", errors = format!("{:?}", errs))),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
}; };
@@ -1167,7 +1167,7 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
if !errs.is_empty() { if !errs.is_empty() {
return Err(Error::new( return Err(Error::new(
eyre!("Failed to compile expression: {:?}", errs), eyre!("{}", t!("util.serde.failed-to-compile-expression", errors = format!("{:?}", errs))),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
}; };
@@ -1182,14 +1182,14 @@ pub fn apply_expr(input: jaq_core::Val, expr: &str) -> Result<jaq_core::Val, Err
.with_kind(crate::ErrorKind::Deserialization)? .with_kind(crate::ErrorKind::Deserialization)?
else { else {
return Err(Error::new( return Err(Error::new(
eyre!("expr returned no results"), eyre!("{}", t!("util.serde.expr-returned-no-results")),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
}; };
if res_iter.next().is_some() { if res_iter.next().is_some() {
return Err(Error::new( return Err(Error::new(
eyre!("expr returned too many results"), eyre!("{}", t!("util.serde.expr-returned-too-many-results")),
crate::ErrorKind::InvalidRequest, crate::ErrorKind::InvalidRequest,
)); ));
} }

View File

@@ -10,7 +10,7 @@ fn map_miette(m: miette::Error) -> Error {
} }
fn noninteractive_err() -> Error { fn noninteractive_err() -> Error {
Error::new( Error::new(
eyre!("Terminal must be in interactive mode for this wizard"), eyre!("{}", t!("util.tui.terminal-must-be-interactive")),
ErrorKind::Filesystem, ErrorKind::Filesystem,
) )
} }
@@ -21,7 +21,7 @@ where
{ {
move |s| { move |s| {
s.parse::<T>() s.parse::<T>()
.map_err(|_| format!("Please enter a valid {what}.")) .map_err(|_| t!("util.tui.enter-valid-value", what = what).to_string())
} }
} }
@@ -50,7 +50,7 @@ pub async fn prompt<T, E: std::fmt::Display, Parse: FnMut(&str) -> Result<T, E>>
} }
} }
ReadlineEvent::Eof | ReadlineEvent::Interrupted => { ReadlineEvent::Eof | ReadlineEvent::Interrupted => {
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
} }
_ => (), _ => (),
} }
@@ -83,7 +83,7 @@ pub async fn prompt_multiline<
Err(e) => writeln!(&mut rl_ctx.shared_writer, "{e}")?, Err(e) => writeln!(&mut rl_ctx.shared_writer, "{e}")?,
}, },
ReadlineEvent::Eof | ReadlineEvent::Interrupted => { ReadlineEvent::Eof | ReadlineEvent::Interrupted => {
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
} }
_ => (), _ => (),
} }
@@ -119,7 +119,7 @@ pub async fn choose_custom_display<'t, T>(
.await .await
.map_err(map_miette)?; .map_err(map_miette)?;
if choice.len() < 1 { if choice.len() < 1 {
return Err(Error::new(eyre!("Aborted"), ErrorKind::Cancelled)); return Err(Error::new(eyre!("{}", t!("util.tui.aborted")), ErrorKind::Cancelled));
} }
let (idx, choice_str) = string_choices let (idx, choice_str) = string_choices
.iter() .iter()
@@ -127,7 +127,7 @@ pub async fn choose_custom_display<'t, T>(
.find(|(_, s)| s.as_str() == choice[0].as_str()) .find(|(_, s)| s.as_str() == choice[0].as_str())
.ok_or_else(|| { .ok_or_else(|| {
Error::new( Error::new(
eyre!("selected choice does not appear in input"), eyre!("{}", t!("util.tui.selected-choice-not-in-input")),
ErrorKind::Incoherent, ErrorKind::Incoherent,
) )
})?; })?;