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]]
name = "cc"
version = "1.2.52"
version = "1.2.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -2726,9 +2726,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fixed-capacity-vec"
@@ -4186,9 +4186,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.83"
version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -6587,7 +6587,7 @@ dependencies = [
[[package]]
name = "rpc-toolkit"
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 = [
"async-stream",
"async-trait",
@@ -6727,9 +6727,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
@@ -6806,7 +6806,7 @@ dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.103.8",
"rustls-webpki 0.103.9",
"subtle",
"zeroize",
]
@@ -6834,9 +6834,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.13.2"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"web-time",
"zeroize",
@@ -6856,7 +6856,7 @@ dependencies = [
"rustls 0.23.36",
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki 0.103.8",
"rustls-webpki 0.103.9",
"security-framework 3.5.1",
"security-framework-sys",
"webpki-root-certs",
@@ -6882,9 +6882,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.8"
version = "0.103.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53"
dependencies = [
"aws-lc-rs",
"ring",
@@ -9556,7 +9556,7 @@ dependencies = [
"paste",
"pin-project",
"rustls-pki-types",
"rustls-webpki 0.103.8",
"rustls-webpki 0.103.9",
"thiserror 2.0.17",
"tokio",
"tokio-util",
@@ -10201,9 +10201,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
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"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
@@ -10225,9 +10225,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [
"cfg-if",
"once_cell",
@@ -10238,11 +10238,12 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.56"
version = "0.4.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
dependencies = [
"cfg-if",
"futures-util",
"js-sys",
"once_cell",
"wasm-bindgen",
@@ -10251,9 +10252,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -10261,9 +10262,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -10274,9 +10275,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.106"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
dependencies = [
"unicode-ident",
]
@@ -10375,9 +10376,9 @@ checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549"
[[package]]
name = "web-sys"
version = "0.3.83"
version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -10974,9 +10975,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
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",
from_fn_async(get_action_input)
.with_display_serializable()
.with_about("Get action input spec")
.with_about("about.get-action-input-spec")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -36,14 +36,14 @@ pub fn action_api<C: Context>() -> ParentHandler<C> {
}
Ok(())
})
.with_about("Run service action")
.with_about("about.run-service-action")
.with_call_remote::<CliContext>(),
)
.subcommand(
"clear-task",
from_fn_async(clear_task)
.no_display()
.with_about("Clear a service task")
.with_about("about.clear-service-task")
.with_call_remote::<CliContext>(),
)
}

View File

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

View File

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

View File

@@ -37,12 +37,12 @@ pub fn backup<C: Context>() -> ParentHandler<C> {
"create",
from_fn_async(backup_bulk::backup_all)
.no_display()
.with_about("Create backup for all packages")
.with_about("about.create-backup-all-packages")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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",
from_fn_async(restore::restore_packages_rpc)
.no_display()
.with_about("Restore package(s) from backup")
.with_about("about.restore-packages-from-backup")
.with_call_remote::<CliContext>(),
)
}

View File

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

View File

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

View File

@@ -143,13 +143,13 @@ pub fn target<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"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(
"list",
from_fn_async(list)
.with_display_serializable()
.with_about("List existing backup targets")
.with_about("about.list-existing-backup-targets")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -159,20 +159,20 @@ pub fn target<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn::<CliContext, _>(|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>(),
)
.subcommand(
"mount",
from_fn_async(mount)
.with_about("Mount backup target")
.with_about("about.mount-backup-target")
.with_call_remote::<CliContext>(),
)
.subcommand(
"umount",
from_fn_async(umount)
.no_display()
.with_about("Unmount backup target")
.with_about("about.unmount-backup-target")
.with_call_remote::<CliContext>(),
)
}

View File

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

View File

@@ -1,9 +1,14 @@
use rust_i18n::t;
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)
}
pub fn removed(name: &str) -> ! {
eprintln!("{name} has been removed");
eprintln!("{}", t!("bins.deprecated.removed", name = name));
std::process::exit(1)
}

View File

@@ -2,6 +2,8 @@ use std::collections::{BTreeMap, VecDeque};
use std::ffi::OsString;
use std::path::Path;
use rust_i18n::t;
pub mod container_cli;
pub mod deprecated;
pub mod registry;
@@ -10,6 +12,72 @@ pub mod start_init;
pub mod startd;
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)]
pub struct MultiExecutable {
default: Option<&'static str>,
@@ -58,7 +126,7 @@ impl MultiExecutable {
if let Some((name, _)) = self.bins.get_key_value(name) {
self.default = Some(*name);
} else {
panic!("{name} does not exist in MultiExecutable");
panic!("{}", t!("bins.mod.does-not-exist", name = name));
}
self
}
@@ -68,6 +136,8 @@ impl MultiExecutable {
}
pub fn execute(&self) {
set_locale();
let mut popped = Vec::with_capacity(2);
let mut args = std::env::args_os().collect::<VecDeque<_>>();
@@ -96,11 +166,15 @@ impl MultiExecutable {
}
let args = std::env::args().collect::<VecDeque<_>>();
eprintln!(
"unknown executable: {}",
args.get(1)
.or_else(|| args.get(0))
.map(|s| s.as_str())
.unwrap_or("N/A")
"{}",
t!(
"bins.mod.unknown-executable",
name = args
.get(1)
.or_else(|| args.get(0))
.map(|s| s.as_str())
.unwrap_or("N/A")
)
);
std::process::exit(1);
}

View File

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

View File

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

View File

@@ -25,7 +25,13 @@ async fn setup_or_init(
if let Some(firmware) = check_for_firmware_update()
.await
.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:?}");
})
.ok()
@@ -33,14 +39,21 @@ async fn setup_or_init(
{
let init_ctx = InitContext::init(config).await?;
let handle = &init_ctx.progress;
let mut update_phase = handle.add_phase("Updating Firmware".into(), Some(10));
let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1));
let mut update_phase =
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);
update_phase.start();
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:?}");
} else {
update_phase.complete();
@@ -96,7 +109,13 @@ async fn setup_or_init(
.invoke(ErrorKind::NotFound)
.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);
}
@@ -105,7 +124,7 @@ async fn setup_or_init(
Some(Err(e)) => return Err(e.clone_output()),
None => {
return Err(Error::new(
eyre!("Setup mode exited before setup completed"),
eyre!("{}", t!("bins.start-init.setup-mode-exited")),
ErrorKind::Unknown,
));
}
@@ -115,7 +134,8 @@ async fn setup_or_init(
let handle = init_ctx.progress.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 rpc_ctx_phases = InitRpcContextPhases::new(&handle);
@@ -147,11 +167,12 @@ async fn setup_or_init(
.with_ctx(|_| (crate::ErrorKind::Filesystem, REPAIR_DISK_PATH))?;
}
disk_phase.complete();
tracing::info!("Loaded Disk");
tracing::info!("{}", t!("bins.start-init.loaded-disk"));
if requires_reboot.0 {
tracing::info!("Rebooting...");
let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1));
tracing::info!("{}", t!("bins.start-init.rebooting"));
let mut reboot_phase =
handle.add_phase(t!("bins.start-init.rebooting").into(), Some(1));
reboot_phase.start();
return Ok(Err(Shutdown {
disk_guid: Some(disk_guid),

View File

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

View File

@@ -6,6 +6,7 @@ use std::time::Duration;
use clap::Parser;
use futures::FutureExt;
use rpc_toolkit::CliApp;
use rust_i18n::t;
use tokio::signal::unix::signal;
use tracing::instrument;
use visit_rs::Visit;
@@ -70,7 +71,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
true
}
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:?}");
false
@@ -92,7 +93,7 @@ async fn inner_main(config: &TunnelConfig) -> Result<(), Error> {
}
.await
{
tracing::error!("error updating webserver bind: {e}");
tracing::error!("{}", t!("bins.tunnel.error-updating-webserver-bind", error = e.to_string()));
tracing::debug!("{e:?}");
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()
.enable_all()
.build()
.expect("failed to initialize runtime");
.expect(&t!("bins.tunnel.failed-to-initialize-runtime"));
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()?)?),
crate::tunnel::api::tunnel_api(),
)
.mutate_command(super::translate_cli)
.run(args)
{
match e.data {

View File

@@ -166,14 +166,14 @@ impl CliContext {
.with_kind(crate::ErrorKind::Pem)?;
let secret = ed25519_dalek::SecretKey::try_from(&pair.secret_key[..]).map_err(|_| {
Error::new(
eyre!("pkcs8 key is of incorrect length"),
eyre!("{}", t!("context.cli.pkcs8-key-incorrect-length")),
ErrorKind::OpenSsl,
)
})?;
return Ok(secret.into())
}
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
))
})
@@ -189,14 +189,14 @@ impl CliContext {
"http" => "ws",
_ => {
return Err(Error::new(
eyre!("Cannot parse scheme from base URL"),
eyre!("{}", t!("context.cli.cannot-parse-scheme-from-base-url")),
crate::ErrorKind::ParseUrl,
)
.into());
}
};
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()
.map_err(|_| eyre!("Url cannot be base"))
.with_kind(crate::ErrorKind::ParseUrl)?

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,45 +17,46 @@ pub fn diagnostic<C: Context>() -> ParentHandler<C> {
.subcommand(
"error",
from_fn(error)
.with_about("Display diagnostic error")
.with_about("about.display-diagnostic-error")
.with_call_remote::<CliContext>(),
)
.subcommand(
"logs",
crate::system::logs::<DiagnosticContext>().with_about("Display OS logs"),
crate::system::logs::<DiagnosticContext>().with_about("about.display-os-logs"),
)
.subcommand(
"logs",
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
.no_display()
.with_about("Display OS logs"),
.with_about("about.display-os-logs"),
)
.subcommand(
"kernel-logs",
crate::system::kernel_logs::<DiagnosticContext>().with_about("Display kernel logs"),
crate::system::kernel_logs::<DiagnosticContext>()
.with_about("about.display-kernel-logs"),
)
.subcommand(
"kernel-logs",
from_fn_async(crate::logs::cli_logs::<DiagnosticContext, Empty>)
.no_display()
.with_about("Display kernal logs"),
.with_about("about.display-kernel-logs"),
)
.subcommand(
"restart",
from_fn(restart)
.no_display()
.with_about("Restart the server")
.with_about("about.restart-server")
.with_call_remote::<CliContext>(),
)
.subcommand(
"disk",
disk::<C>().with_about("Command to remove disk from filesystem"),
disk::<C>().with_about("about.command-remove-disk-filesystem"),
)
.subcommand(
"rebuild",
from_fn_async(rebuild)
.no_display()
.with_about("Teardown and rebuild service containers")
.with_about("about.teardown-rebuild-containers")
.with_call_remote::<CliContext>(),
)
}
@@ -89,7 +90,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
from_fn_async(forget_disk::<RpcContext>).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(
@@ -97,7 +98,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
CallRemoteHandler::<CliContext, _, _>::new(
from_fn_async(|_: RpcContext| repair())
.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 futures::FutureExt;
use futures::future::BoxFuture;
use rust_i18n::t;
use tokio::process::Command;
use tracing::instrument;
@@ -62,33 +63,31 @@ async fn e2fsck_runner(
let e2fsck_stderr = String::from_utf8(e2fsck_out.stderr)?;
let code = e2fsck_out.status.code().ok_or_else(|| {
Error::new(
eyre!("e2fsck: process terminated by signal"),
eyre!("{}", t!("disk.fsck.process-terminated-by-signal")),
crate::ErrorKind::DiskManagement,
)
})?;
if code & 4 != 0 {
tracing::error!(
"some filesystem errors NOT corrected on {}:\n{}",
logicalname.as_ref().display(),
e2fsck_stderr,
"{}",
t!("disk.fsck.errors-not-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr),
);
} else if code & 1 != 0 {
tracing::warn!(
"filesystem errors corrected on {}:\n{}",
logicalname.as_ref().display(),
e2fsck_stderr,
"{}",
t!("disk.fsck.errors-corrected", device = logicalname.as_ref().display(), stderr = e2fsck_stderr),
);
}
if code < 8 {
if code & 2 != 0 {
tracing::warn!("reboot required");
tracing::warn!("{}", t!("disk.fsck.reboot-required"));
Ok(RequiresReboot(true))
} else {
Ok(RequiresReboot(false))
}
} else {
Err(Error::new(
eyre!("e2fsck: {}", e2fsck_stderr),
eyre!("{}", t!("disk.fsck.e2fsck-error", stderr = e2fsck_stderr)),
crate::ErrorKind::DiskManagement,
))
}

View File

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

View File

@@ -51,7 +51,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
from_fn_async(list)
.with_display_serializable()
.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>(),
)
.subcommand("repair", from_fn_async(|_: C| repair()).no_cli())
@@ -60,7 +60,7 @@ pub fn disk<C: Context>() -> ParentHandler<C> {
CallRemoteHandler::<CliContext, _, _>::new(
from_fn_async(|_: RpcContext| repair())
.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,
) -> Result<(), Error> {
tracing::info!(
"Binding {} to {}",
src.as_ref().display(),
dst.as_ref().display()
"{}",
t!("disk.mount.binding", src = src.as_ref().display(), dst = dst.as_ref().display())
);
if is_mountpoint(&dst).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)
.join(path.as_ref().strip_prefix("/dev").map_err(|_| {
Error::new(
eyre!("not a canonical block device"),
eyre!("{}", t!("disk.util.not-canonical-block-device")),
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)
.join(path.as_ref().strip_prefix("/dev").map_err(|_| {
Error::new(
eyre!("not a canonical block device"),
eyre!("{}", t!("disk.util.not-canonical-block-device")),
crate::ErrorKind::BlockDevice,
)
})?)
@@ -374,23 +374,22 @@ async fn disk_info(disk: PathBuf) -> DiskInfo {
.await
.map_err(|e| {
tracing::warn!(
"Could not get partition table of {}: {}",
disk.display(),
e.source
"{}",
t!("disk.util.could-not-get-partition-table", disk = disk.display(), error = e.source)
)
})
.unwrap_or_default();
let vendor = get_vendor(&disk)
.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();
let model = get_model(&disk)
.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();
let capacity = get_capacity(&disk)
.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();
DiskInfo {
logicalname: disk,
@@ -407,21 +406,21 @@ async fn part_info(part: PathBuf) -> PartitionInfo {
let mut start_os = BTreeMap::new();
let label = get_label(&part)
.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();
let capacity = get_capacity(&part)
.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();
let mut used = None;
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) => {
used = get_used(mount_guard.path())
.await
.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();
match recovery_info(mount_guard.path()).await {
@@ -429,11 +428,11 @@ async fn part_info(part: PathBuf) -> PartitionInfo {
start_os = a;
}
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 {
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));
}
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::{
INVALID_PARAMS_ERROR, INVALID_REQUEST_ERROR, METHOD_NOT_FOUND_ERROR, PARSE_ERROR, RpcError,
};
use rust_i18n::t;
use serde::{Deserialize, Serialize};
use tokio::task::JoinHandle;
use tokio_rustls::rustls;
@@ -100,94 +101,94 @@ pub enum ErrorKind {
SetSysInfo = 79,
}
impl ErrorKind {
pub fn as_str(&self) -> &'static str {
pub fn as_str(&self) -> String {
use ErrorKind::*;
match self {
Unknown => "Unknown Error",
Filesystem => "Filesystem I/O Error",
Docker => "Docker Error",
ConfigSpecViolation => "Config Spec Violation",
ConfigRulesViolation => "Config Rules Violation",
NotFound => "Not Found",
IncorrectPassword => "Incorrect Password",
VersionIncompatible => "Version Incompatible",
Network => "Network Error",
Registry => "Registry Error",
Serialization => "Serialization Error",
Deserialization => "Deserialization Error",
Utf8 => "UTF-8 Parse Error",
ParseVersion => "Version Parsing Error",
IncorrectDisk => "Incorrect Disk",
// Nginx => "Nginx Error",
Dependency => "Dependency Error",
ParseS9pk => "S9PK Parsing Error",
ParseUrl => "URL Parsing Error",
DiskNotAvailable => "Disk Not Available",
BlockDevice => "Block Device Error",
InvalidOnionAddress => "Invalid Onion Address",
Pack => "Pack Error",
ValidateS9pk => "S9PK Validation Error",
DiskCorrupted => "Disk Corrupted", // Remove
Tor => "Tor Daemon Error",
ConfigGen => "Config Generation Error",
ParseNumber => "Number Parsing Error",
Database => "Database Error",
InvalidId => "Invalid ID",
InvalidSignature => "Invalid Signature",
Backup => "Backup Error",
Restore => "Restore Error",
Authorization => "Unauthorized",
AutoConfigure => "Auto-Configure Error",
Action => "Action Failed",
RateLimited => "Rate Limited",
InvalidRequest => "Invalid Request",
MigrationFailed => "Migration Failed",
Uninitialized => "Uninitialized",
ParseNetAddress => "Net Address Parsing Error",
ParseSshKey => "SSH Key Parsing Error",
SoundError => "Sound Interface Error",
ParseTimestamp => "Timestamp Parsing Error",
ParseSysInfo => "System Info Parsing Error",
Wifi => "WiFi Internal Error",
Journald => "Journald Error",
DiskManagement => "Disk Management Error",
OpenSsl => "OpenSSL Internal Error",
PasswordHashGeneration => "Password Hash Generation Error",
DiagnosticMode => "Server is in Diagnostic Mode",
ParseDbField => "Database Field Parse Error",
Duplicate => "Duplication Error",
MultipleErrors => "Multiple Errors",
Incoherent => "Incoherent",
InvalidBackupTargetId => "Invalid Backup Target ID",
ProductKeyMismatch => "Incompatible Product Keys",
LanPortConflict => "Incompatible LAN Port Configuration",
Javascript => "Javascript Engine Error",
Pem => "PEM Encoding Error",
TLSInit => "TLS Backend Initialization Error",
Ascii => "ASCII Parse Error",
MissingHeader => "Missing Header",
Grub => "Grub Error",
Systemd => "Systemd Error",
OpenSsh => "OpenSSH Error",
Zram => "Zram Error",
Lshw => "LSHW Error",
CpuSettings => "CPU Settings Error",
Firmware => "Firmware Error",
Timeout => "Timeout Error",
Lxc => "LXC Error",
Cancelled => "Cancelled",
Git => "Git Error",
DBus => "DBus Error",
InstallFailed => "Install Failed",
UpdateFailed => "Update Failed",
Smtp => "SMTP Error",
SetSysInfo => "Error Setting System Info",
}
Unknown => t!("error.unknown"),
Filesystem => t!("error.filesystem"),
Docker => t!("error.docker"),
ConfigSpecViolation => t!("error.config-spec-violation"),
ConfigRulesViolation => t!("error.config-rules-violation"),
NotFound => t!("error.not-found"),
IncorrectPassword => t!("error.incorrect-password"),
VersionIncompatible => t!("error.version-incompatible"),
Network => t!("error.network"),
Registry => t!("error.registry"),
Serialization => t!("error.serialization"),
Deserialization => t!("error.deserialization"),
Utf8 => t!("error.utf8"),
ParseVersion => t!("error.parse-version"),
IncorrectDisk => t!("error.incorrect-disk"),
// Nginx => t!("error.nginx"),
Dependency => t!("error.dependency"),
ParseS9pk => t!("error.parse-s9pk"),
ParseUrl => t!("error.parse-url"),
DiskNotAvailable => t!("error.disk-not-available"),
BlockDevice => t!("error.block-device"),
InvalidOnionAddress => t!("error.invalid-onion-address"),
Pack => t!("error.pack"),
ValidateS9pk => t!("error.validate-s9pk"),
DiskCorrupted => t!("error.disk-corrupted"), // Remove
Tor => t!("error.tor"),
ConfigGen => t!("error.config-gen"),
ParseNumber => t!("error.parse-number"),
Database => t!("error.database"),
InvalidId => t!("error.invalid-id"),
InvalidSignature => t!("error.invalid-signature"),
Backup => t!("error.backup"),
Restore => t!("error.restore"),
Authorization => t!("error.authorization"),
AutoConfigure => t!("error.auto-configure"),
Action => t!("error.action"),
RateLimited => t!("error.rate-limited"),
InvalidRequest => t!("error.invalid-request"),
MigrationFailed => t!("error.migration-failed"),
Uninitialized => t!("error.uninitialized"),
ParseNetAddress => t!("error.parse-net-address"),
ParseSshKey => t!("error.parse-ssh-key"),
SoundError => t!("error.sound-error"),
ParseTimestamp => t!("error.parse-timestamp"),
ParseSysInfo => t!("error.parse-sys-info"),
Wifi => t!("error.wifi"),
Journald => t!("error.journald"),
DiskManagement => t!("error.disk-management"),
OpenSsl => t!("error.openssl"),
PasswordHashGeneration => t!("error.password-hash-generation"),
DiagnosticMode => t!("error.diagnostic-mode"),
ParseDbField => t!("error.parse-db-field"),
Duplicate => t!("error.duplicate"),
MultipleErrors => t!("error.multiple-errors"),
Incoherent => t!("error.incoherent"),
InvalidBackupTargetId => t!("error.invalid-backup-target-id"),
ProductKeyMismatch => t!("error.product-key-mismatch"),
LanPortConflict => t!("error.lan-port-conflict"),
Javascript => t!("error.javascript"),
Pem => t!("error.pem"),
TLSInit => t!("error.tls-init"),
Ascii => t!("error.ascii"),
MissingHeader => t!("error.missing-header"),
Grub => t!("error.grub"),
Systemd => t!("error.systemd"),
OpenSsh => t!("error.openssh"),
Zram => t!("error.zram"),
Lshw => t!("error.lshw"),
CpuSettings => t!("error.cpu-settings"),
Firmware => t!("error.firmware"),
Timeout => t!("error.timeout"),
Lxc => t!("error.lxc"),
Cancelled => t!("error.cancelled"),
Git => t!("error.git"),
DBus => t!("error.dbus"),
InstallFailed => t!("error.install-failed"),
UpdateFailed => t!("error.update-failed"),
Smtp => t!("error.smtp"),
SetSysInfo => t!("error.set-sys-info"),
}.to_string()
}
}
impl Display for ErrorKind {
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 {
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 {
@@ -209,7 +210,7 @@ impl Debug for Error {
write!(
f,
"{}: {:?}",
self.kind.as_str(),
&self.kind.as_str(),
self.debug.as_ref().unwrap_or(&self.source)
)
}

View File

@@ -81,26 +81,28 @@ impl InitPhases {
pub fn new(handle: &FullProgressTracker) -> Self {
Self {
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 {
None
},
local_auth: handle.add_phase("Enabling local authentication".into(), Some(1)),
load_database: handle.add_phase("Loading database".into(), Some(5)),
load_ssh_keys: handle.add_phase("Loading SSH Keys".into(), Some(1)),
start_net: handle.add_phase("Starting network controller".into(), Some(1)),
mount_logs: handle.add_phase("Switching logs to write to data drive".into(), Some(1)),
load_ca_cert: handle.add_phase("Loading CA certificate".into(), Some(1)),
load_wifi: handle.add_phase("Loading WiFi configuration".into(), Some(1)),
init_tmp: handle.add_phase("Initializing temporary files".into(), Some(1)),
set_governor: handle.add_phase("Setting CPU performance profile".into(), Some(1)),
sync_clock: handle.add_phase("Synchronizing system clock".into(), Some(10)),
enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)),
update_server_info: handle.add_phase("Updating server info".into(), Some(1)),
launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)),
validate_db: handle.add_phase("Validating database".into(), Some(1)),
local_auth: handle.add_phase(t!("init.enabling-local-auth").into(), Some(1)),
load_database: handle.add_phase(t!("init.loading-database").into(), Some(5)),
load_ssh_keys: handle.add_phase(t!("init.loading-ssh-keys").into(), Some(1)),
start_net: handle.add_phase(t!("init.starting-network-controller").into(), Some(1)),
mount_logs: handle.add_phase(t!("init.switching-logs-to-data-drive").into(), Some(1)),
load_ca_cert: handle.add_phase(t!("init.loading-ca-certificate").into(), Some(1)),
load_wifi: handle.add_phase(t!("init.loading-wifi-configuration").into(), Some(1)),
init_tmp: handle.add_phase(t!("init.initializing-temporary-files").into(), Some(1)),
set_governor: handle
.add_phase(t!("init.setting-cpu-performance-profile").into(), Some(1)),
sync_clock: handle.add_phase(t!("init.synchronizing-system-clock").into(), Some(10)),
enable_zram: handle.add_phase(t!("init.enabling-zram").into(), Some(1)),
update_server_info: handle.add_phase(t!("init.updating-server-info").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() {
Some(handle.add_phase("Running postinit.sh".into(), Some(5)))
Some(handle.add_phase(t!("init.running-postinit").into(), Some(5)))
} else {
None
},
@@ -127,7 +129,14 @@ pub async fn run_script<P: AsRef<Path>>(path: P, mut progress: PhaseProgressTrac
}
.await
{
tracing::error!("Error Running {}: {}", script.display(), e);
tracing::error!(
"{}",
t!(
"init.error-running-script",
script = script.display(),
error = e
)
);
tracing::debug!("{:?}", e);
}
progress.complete();
@@ -230,6 +239,7 @@ pub async fn init(
.arg("-R")
.arg("+C")
.arg("/var/log/journal")
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::Filesystem)
.await
{
@@ -314,14 +324,17 @@ pub async fn init(
{
Some(governor)
} else {
tracing::warn!("CPU Governor \"{governor}\" Not Available");
tracing::warn!(
"{}",
t!("init.cpu-governor-not-available", governor = governor)
);
None
}
} else {
cpupower::get_preferred_governor().await?
};
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?;
}
set_governor.complete();
@@ -349,14 +362,14 @@ pub async fn init(
}
}
if !ntp_synced {
tracing::warn!("Timed out waiting for system time to synchronize");
tracing::warn!("{}", t!("init.clock-sync-timeout"));
}
sync_clock.complete();
enable_zram.start();
if server_info.as_zram().de()? {
crate::system::enable_zram().await?;
tracing::info!("Enabled ZRAM");
tracing::info!("{}", t!("init.enabled-zram"));
}
enable_zram.complete();
@@ -404,7 +417,7 @@ pub async fn init(
run_script("/media/startos/config/postinit.sh", progress).await;
}
tracing::info!("System initialized.");
tracing::info!("{}", t!("init.system-initialized"));
Ok(InitResult {
net_ctrl,
@@ -416,30 +429,30 @@ pub fn init_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"logs",
crate::system::logs::<InitContext>().with_about("Disply OS logs"),
crate::system::logs::<InitContext>().with_about("about.display-os-logs"),
)
.subcommand(
"logs",
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
.no_display()
.with_about("Display OS logs"),
.with_about("about.display-os-logs"),
)
.subcommand(
"kernel-logs",
crate::system::kernel_logs::<InitContext>().with_about("Display kernel logs"),
crate::system::kernel_logs::<InitContext>().with_about("about.display-kernel-logs"),
)
.subcommand(
"kernel-logs",
from_fn_async(crate::logs::cli_logs::<InitContext, Empty>)
.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(cli_init_progress)
.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 {
tracing::error!("error closing init progress websocket: {e}");
tracing::error!("{}", t!("init.error-closing-websocket", error = e));
tracing::debug!("{e:?}");
}
},
@@ -526,7 +539,7 @@ pub async fn cli_init_progress(
.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)? {
if let tokio_tungstenite::tungstenite::Message::Text(msg) = msg {
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()
.subcommand(
"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(
"echo",
from_fn(echo::<RpcContext>)
.with_metadata("authenticated", Value::Bool(false))
.with_about("Echo a message")
.with_about("about.echo-message")
.with_call_remote::<CliContext>(),
)
.subcommand(
"state",
from_fn(|_: RpcContext| Ok::<_, Error>(ApiState::Running))
.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>(),
)
.subcommand(
"state",
from_fn(|_: InitContext| Ok::<_, Error>(ApiState::Initializing))
.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>(),
)
.subcommand(
"state",
from_fn(|_: DiagnosticContext| Ok::<_, Error>(ApiState::Error))
.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>(),
)
.subcommand(
"server",
server::<C>()
.with_about("Commands related to the server i.e. restart, update, and shutdown"),
)
.subcommand("server", server::<C>().with_about("about.commands-server"))
.subcommand(
"package",
package::<C>().with_about("Commands related to packages"),
package::<C>().with_about("about.commands-packages"),
)
.subcommand(
"net",
net::net_api::<C>().with_about("Network commands related to tor and dhcp"),
net::net_api::<C>().with_about("about.network-commands"),
)
.subcommand(
"auth",
auth::auth::<C, RpcContext>()
.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"),
auth::auth::<C, RpcContext>().with_about("about.commands-authentication"),
)
.subcommand("db", db::db::<C>().with_about("about.commands-db"))
.subcommand("ssh", ssh::ssh::<C>().with_about("about.commands-ssh-keys"))
.subcommand(
"wifi",
net::wifi::wifi::<C>()
.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"),
net::wifi::wifi::<C>().with_about("about.commands-wifi"),
)
.subcommand("disk", disk::disk::<C>().with_about("about.commands-disk"))
.subcommand(
"notification",
notifications::notification::<C>().with_about("Create, delete, or list notifications"),
notifications::notification::<C>().with_about("about.commands-notifications"),
)
.subcommand(
"backup",
backup::backup::<C>()
.with_about("Commands related to backup creation and backup targets"),
backup::backup::<C>().with_about("about.commands-backup"),
)
.subcommand(
"registry",
@@ -219,7 +202,7 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
)
.subcommand(
"registry",
registry::registry_api::<CliContext>().with_about("Commands related to the registry"),
registry::registry_api::<CliContext>().with_about("about.commands-registry"),
)
.subcommand(
"tunnel",
@@ -228,31 +211,26 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
)
.subcommand(
"tunnel",
tunnel::api::tunnel_api::<CliContext>().with_about("Commands related to StartTunnel"),
)
.subcommand(
"s9pk",
s9pk::rpc::s9pk().with_about("Commands for interacting with s9pk files"),
tunnel::api::tunnel_api::<CliContext>().with_about("about.commands-tunnel"),
)
.subcommand("s9pk", s9pk::rpc::s9pk().with_about("about.commands-s9pk"))
.subcommand(
"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(
"init-key",
from_fn_async(developer::init)
.no_display()
.with_about("Create developer key if it doesn't exist"),
.with_about("about.create-developer-key"),
)
.subcommand(
"pubkey",
from_fn_blocking(developer::pubkey)
.with_about("Get public key for developer private key"),
from_fn_blocking(developer::pubkey).with_about("about.get-developer-pubkey"),
)
.subcommand(
"diagnostic",
diagnostic::diagnostic::<C>()
.with_about("Commands to display logs, restart the server, etc"),
diagnostic::diagnostic::<C>().with_about("about.commands-diagnostic"),
)
.subcommand("init", init::init_api::<C>())
.subcommand("setup", setup::setup::<C>());
@@ -265,7 +243,7 @@ pub fn main_api<C: Context>() -> ParentHandler<C> {
"flash-os",
from_fn_async(os_install::cli_install_os)
.no_display()
.with_about("Flash StartOS to a disk from a squashfs"),
.with_about("about.flash-startos"),
);
}
api
@@ -280,29 +258,32 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, result| {
system::display_time(handle.params, result)
})
.with_about("Display current time and server uptime")
.with_call_remote::<CliContext>()
.with_about("about.display-time-uptime")
.with_call_remote::<CliContext>(),
)
.subcommand(
"experimental",
system::experimental::<C>()
.with_about("Commands related to configuring experimental options such as zram and cpu governor"),
system::experimental::<C>().with_about("about.commands-experimental"),
)
.subcommand(
"logs",
system::logs::<RpcContext>().with_about("Display OS logs"),
system::logs::<RpcContext>().with_about("about.display-os-logs"),
)
.subcommand(
"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(
"kernel-logs",
system::kernel_logs::<RpcContext>().with_about("Display Kernel logs"),
system::kernel_logs::<RpcContext>().with_about("about.display-kernel-logs"),
)
.subcommand(
"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(
"metrics",
@@ -310,35 +291,31 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.root_handler(
from_fn_async(system::metrics)
.with_display_serializable()
.with_about("Display information about the server i.e. temperature, RAM, CPU, and disk usage")
.with_call_remote::<CliContext>()
)
.subcommand(
"follow",
from_fn_async(system::metrics_follow)
.no_cli()
.with_about("about.display-server-metrics")
.with_call_remote::<CliContext>(),
)
.subcommand("follow", from_fn_async(system::metrics_follow).no_cli()),
)
.subcommand(
"shutdown",
from_fn_async(shutdown::shutdown)
.no_display()
.with_about("Shutdown the server")
.with_call_remote::<CliContext>()
.with_about("about.shutdown-server")
.with_call_remote::<CliContext>(),
)
.subcommand(
"restart",
from_fn_async(shutdown::restart)
.no_display()
.with_about("Restart the server")
.with_call_remote::<CliContext>()
.with_about("about.restart-server")
.with_call_remote::<CliContext>(),
)
.subcommand(
"rebuild",
from_fn_async(shutdown::rebuild)
.no_display()
.with_about("Teardown and rebuild service containers")
.with_call_remote::<CliContext>()
.with_about("about.teardown-rebuild-containers")
.with_call_remote::<CliContext>(),
)
.subcommand(
"update",
@@ -348,7 +325,9 @@ pub fn server<C: Context>() -> ParentHandler<C> {
)
.subcommand(
"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(
"update-firmware",
@@ -363,37 +342,41 @@ pub fn server<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|_handle, 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_call_remote::<CliContext>()
.with_about("about.update-firmware")
.with_call_remote::<CliContext>(),
)
.subcommand(
"set-smtp",
from_fn_async(system::set_system_smtp)
.no_display()
.with_about("Set system smtp server and credentials")
.with_call_remote::<CliContext>()
.with_about("about.set-smtp")
.with_call_remote::<CliContext>(),
)
.subcommand(
"test-smtp",
"test-smtp",
from_fn_async(system::test_smtp)
.no_display()
.with_about("Send test email using provided smtp server and credentials")
.with_call_remote::<CliContext>()
.with_about("about.test-smtp")
.with_call_remote::<CliContext>(),
)
.subcommand(
"clear-smtp",
from_fn_async(system::clear_system_smtp)
.no_display()
.with_about("Remove system smtp server and credentials")
.with_call_remote::<CliContext>()
).subcommand("host", net::host::server_host_api::<C>().with_about("Commands for modifying the host for the system ui"))
.with_about("about.clear-smtp")
.with_call_remote::<CliContext>(),
)
.subcommand(
"host",
net::host::server_host_api::<C>().with_about("about.commands-host-system-ui"),
)
}
pub fn package<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"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(
"install",
@@ -411,13 +394,13 @@ pub fn package<C: Context>() -> ParentHandler<C> {
"install",
from_fn_async_local(install::cli_install)
.no_display()
.with_about("Install a package from a marketplace or via sideloading"),
.with_about("about.install-package"),
)
.subcommand(
"cancel-install",
from_fn(install::cancel_install)
.no_display()
.with_about("Cancel an install of a package")
.with_about("about.cancel-install-package")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -425,21 +408,21 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(install::uninstall)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Remove a package")
.with_about("about.remove-package")
.with_call_remote::<CliContext>(),
)
.subcommand(
"list",
from_fn_async(install::list)
.with_display_serializable()
.with_about("List installed packages")
.with_about("about.list-installed-packages")
.with_call_remote::<CliContext>(),
)
.subcommand(
"installed-version",
from_fn_async(install::installed_version)
.with_display_serializable()
.with_about("Display installed version for a PackageId")
.with_about("about.display-installed-version")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -447,7 +430,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::start)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Start a service")
.with_about("about.start-service")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -455,7 +438,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::stop)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Stop a service")
.with_about("about.stop-service")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -463,7 +446,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(control::restart)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Restart a service")
.with_about("about.restart-service")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -471,7 +454,7 @@ pub fn package<C: Context>() -> ParentHandler<C> {
from_fn_async(service::rebuild)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Rebuild service container")
.with_about("about.rebuild-service-container")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -511,35 +494,34 @@ pub fn package<C: Context>() -> ParentHandler<C> {
table.print_tty(false)?;
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>(),
)
.subcommand("logs", logs::package_logs())
.subcommand(
"logs",
logs::package_logs().with_about("Display package logs"),
logs::package_logs().with_about("about.display-package-logs"),
)
.subcommand(
"logs",
from_fn_async(logs::cli_logs::<RpcContext, logs::PackageIdParams>)
.no_display()
.with_about("Display package logs"),
.with_about("about.display-package-logs"),
)
.subcommand(
"backup",
backup::package_backup::<C>()
.with_about("Commands for restoring package(s) from backup"),
backup::package_backup::<C>().with_about("about.commands-restore-backup"),
)
.subcommand(
"attach",
from_fn_async(service::attach)
.with_metadata("get_session", Value::Bool(true))
.with_about("Execute commands within a service container")
.with_about("about.execute-commands-container")
.no_cli(),
)
.subcommand("attach", from_fn_async(service::cli_attach).no_display())
.subcommand(
"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 out =
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());
@@ -700,7 +700,7 @@ pub async fn follow_logs<Context: AsRef<RpcContinuations>>(
RpcContinuation::ws(
move |socket| async move {
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),

View File

@@ -141,7 +141,7 @@ impl LxcManager {
> 0
{
return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"),
eyre!("{}", t!("lxc.mod.rootfs-not-empty")),
ErrorKind::InvalidRequest,
));
}
@@ -249,6 +249,7 @@ impl LxcContainer {
.arg("-R")
.arg("+C")
.arg(&log_mount_point)
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::Filesystem)
.await
{
@@ -381,7 +382,7 @@ impl LxcContainer {
}
if start.elapsed() > CONTAINER_DHCP_TIMEOUT {
return Err(Error::new(
eyre!("Timed out waiting for container to acquire DHCP lease"),
eyre!("{}", t!("lxc.mod.dhcp-timeout")),
ErrorKind::Timeout,
));
}
@@ -407,9 +408,12 @@ impl LxcContainer {
if !output.status.success() {
return Err(Error::new(
eyre!(
"Command failed with exit code: {:?} \n Message: {:?}",
output.status.code(),
String::from_utf8(output.stderr)
"{}",
t!(
"lxc.mod.command-failed",
code = format!("{:?}", output.status.code()),
message = format!("{:?}", String::from_utf8(output.stderr))
)
),
ErrorKind::Docker,
));
@@ -437,7 +441,7 @@ impl LxcContainer {
> 0
{
return Err(Error::new(
eyre!("rootfs is not empty, refusing to delete"),
eyre!("{}", t!("lxc.mod.rootfs-not-empty")),
ErrorKind::InvalidRequest,
));
}
@@ -473,13 +477,19 @@ impl LxcContainer {
.await
);
return Err(Error::new(
eyre!("timed out waiting for socket"),
eyre!("{}", t!("lxc.mod.socket-timeout")),
ErrorKind::Timeout,
));
}
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))
}
@@ -569,8 +579,11 @@ impl Drop for LxcContainer {
fn drop(&mut self) {
if !self.exited {
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 guid = std::mem::take(&mut self.guid);
@@ -589,16 +602,25 @@ impl Drop for LxcContainer {
}
.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:?}")
}
rootfs.unmount(true).await.log_err();
drop(guid);
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:?}")
} 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 {
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> {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -209,7 +209,7 @@ pub fn binding<C: Context, Kind: HostApiKind>()
Ok(())
})
.with_about("List bindinges for this host")
.with_about("about.list-bindings-for-host")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -218,7 +218,7 @@ pub fn binding<C: Context, Kind: HostApiKind>()
.with_metadata("sync_db", Value::Bool(true))
.with_inherited(Kind::inheritance)
.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>(),
)
}

View File

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

View File

@@ -23,32 +23,29 @@ pub mod wifi;
pub fn net_api<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
.subcommand(
"tor",
tor::tor_api::<C>().with_about("Tor commands such as list-services, logs, and reset"),
)
.subcommand("tor", tor::tor_api::<C>().with_about("about.tor-commands"))
.subcommand(
"acme",
acme::acme_api::<C>().with_about("Setup automatic clearnet certificate acquisition"),
acme::acme_api::<C>().with_about("about.setup-acme-certificate"),
)
.subcommand(
"dns",
dns::dns_api::<C>().with_about("Manage and query DNS"),
dns::dns_api::<C>().with_about("about.manage-query-dns"),
)
.subcommand(
"forward",
forward::forward_api::<C>().with_about("Manage port forwards"),
forward::forward_api::<C>().with_about("about.manage-port-forwards"),
)
.subcommand(
"gateway",
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"),
gateway::gateway_api::<C>().with_about("about.view-edit-gateway-configs"),
)
.subcommand(
"tunnel",
tunnel::tunnel_api::<C>().with_about("Manage tunnels"),
tunnel::tunnel_api::<C>().with_about("about.manage-tunnels"),
)
.subcommand(
"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()
.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(
tor_llcrypto::pk::ed25519::ExpandedKeypair::from_secret_key_bytes(bytes)
.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(),
))
@@ -226,19 +229,19 @@ pub fn tor_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_services)
.with_display_serializable()
.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>(),
)
.subcommand(
"reset",
from_fn_async(reset)
.no_display()
.with_about("Reset Tor daemon")
.with_about("about.reset-tor-daemon")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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(
"generate",
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>(),
)
.subcommand(
"add",
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>(),
)
.subcommand(
@@ -265,7 +268,7 @@ pub fn key<C: Context>() -> ParentHandler<C> {
}
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>(),
)
}
@@ -447,9 +450,13 @@ impl TorController {
if prev_inst.elapsed() > BOOTSTRAP_PROGRESS_TIMEOUT {
return Err(Error::new(
eyre!(
"Bootstrap has not made progress for {}",
crate::util::serde::Duration::from(
BOOTSTRAP_PROGRESS_TIMEOUT
"{}",
t!(
"net.tor.bootstrap-no-progress",
duration = crate::util::serde::Duration::from(
BOOTSTRAP_PROGRESS_TIMEOUT
)
.to_string()
)
),
ErrorKind::Tor,
@@ -466,7 +473,10 @@ impl TorController {
res = bootstrap_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:?}");
} else {
bootstrapper_client.send_modify(|_| ());
@@ -516,7 +526,13 @@ impl TorController {
}
.await
{
tracing::error!("Tor Health Error: {e}");
tracing::error!(
"{}",
t!(
"net.tor.health-error",
error = e.to_string()
)
);
tracing::debug!("{e:?}");
}
});
@@ -529,7 +545,10 @@ impl TorController {
}
}
Err(Error::new(
eyre!("status event stream ended"),
eyre!(
"{}",
t!("net.tor.status-stream-ended")
),
ErrorKind::Tor,
))
})
@@ -560,13 +579,19 @@ impl TorController {
}
.await
{
tracing::error!("Tor Client Health Error: {e}");
tracing::error!(
"{}",
t!("net.tor.client-health-error", error = e.to_string())
);
tracing::debug!("{e:?}");
}
}
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
{
tracing::error!("Tor Bootstrapper Error: {e}");
tracing::error!(
"{}",
t!("net.tor.bootstrapper-error", error = e.to_string())
);
tracing::debug!("{e:?}");
}
if let Err::<(), Error>(e) = async {
@@ -592,7 +620,10 @@ impl TorController {
}
.await
{
tracing::error!("Tor Client Creation Error: {e}");
tracing::error!(
"{}",
t!("net.tor.client-creation-error", error = e.to_string())
);
tracing::debug!("{e:?}");
}
}
@@ -684,7 +715,10 @@ impl TorController {
.await
.with_kind(ErrorKind::Network)?;
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:?}");
}
Ok(Box::new(tcp_stream))
@@ -812,7 +846,7 @@ impl OnionService {
.await
.with_kind(ErrorKind::Network)?;
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:?}");
}
let mut incoming = req
@@ -852,7 +886,7 @@ impl OnionService {
}
.await
{
tracing::error!("Tor Client Error: {e}");
tracing::error!("{}", t!("net.tor.client-error", error = e.to_string()));
tracing::debug!("{e:?}");
}
}

View File

@@ -249,26 +249,26 @@ pub fn tor_api<C: Context>() -> ParentHandler<C> {
from_fn_async(list_services)
.with_display_serializable()
.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>(),
)
.subcommand("logs", logs().with_about("Display Tor logs"))
.subcommand("logs", logs().with_about("about.display-tor-logs"))
.subcommand(
"logs",
from_fn_async(crate::logs::cli_logs::<RpcContext, Empty>)
.no_display()
.with_about("Display Tor logs"),
.with_about("about.display-tor-logs"),
)
.subcommand(
"reset",
from_fn_async(reset)
.no_display()
.with_about("Reset Tor daemon")
.with_about("about.reset-tor-daemon")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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(
"generate",
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>(),
)
.subcommand(
"add",
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>(),
)
.subcommand(
@@ -295,7 +295,7 @@ pub fn key<C: Context>() -> ParentHandler<C> {
}
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>(),
)
}

View File

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

View File

@@ -30,7 +30,7 @@ type WifiManager = Arc<RwLock<Option<WpaCli>>>;
// Ok(wifi_manager)
// } else {
// Err(Error::new(
// color_eyre::eyre::eyre!("No WiFi interface available"),
// color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
// ErrorKind::Wifi,
// ))
// }
@@ -42,28 +42,28 @@ pub fn wifi<C: Context>() -> ParentHandler<C> {
"set-enabled",
from_fn_async(set_enabled)
.no_display()
.with_about("Enable or disable wifi")
.with_about("about.enable-disable-wifi")
.with_call_remote::<CliContext>(),
)
.subcommand(
"add",
from_fn_async(add)
.no_display()
.with_about("Add wifi ssid and password")
.with_about("about.add-wifi-ssid-password")
.with_call_remote::<CliContext>(),
)
.subcommand(
"connect",
from_fn_async(connect)
.no_display()
.with_about("Connect to wifi network")
.with_about("about.connect-wifi-network")
.with_call_remote::<CliContext>(),
)
.subcommand(
"remove",
from_fn_async(remove)
.no_display()
.with_about("Remove a wifi network")
.with_about("about.remove-wifi-network")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -71,16 +71,16 @@ pub fn wifi<C: Context>() -> ParentHandler<C> {
from_fn_async(get)
.with_display_serializable()
.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>(),
)
.subcommand(
"country",
country::<C>().with_about("Command to set country"),
country::<C>().with_about("about.command-set-country"),
)
.subcommand(
"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)
.with_display_serializable()
.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>(),
)
}
@@ -143,7 +143,7 @@ pub fn country<C: Context>() -> ParentHandler<C> {
"set",
from_fn_async(set_country)
.no_display()
.with_about("Set Country")
.with_about("about.set-country")
.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();
if !ssid.is_ascii() {
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,
));
}
if !password.is_ascii() {
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,
));
}
@@ -176,11 +176,11 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re
ssid: &Ssid,
password: &Psk,
) -> 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 wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
@@ -195,10 +195,17 @@ pub async fn add(ctx: RpcContext, AddParams { ssid, password }: AddParams) -> Re
)
.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);
return Err(Error::new(
color_eyre::eyre::eyre!("Failed adding {}", ssid),
color_eyre::eyre::eyre!("{}", t!("net.wifi.failed-adding", ssid = &ssid)),
ErrorKind::Wifi,
));
}
@@ -230,7 +237,7 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result
let wifi_manager = ctx.wifi_manager.clone();
if !ssid.is_ascii() {
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,
));
}
@@ -242,19 +249,19 @@ pub async fn connect(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
let current = wpa_supplicant.get_current_network().await?;
let connected = wpa_supplicant.select_network(db.clone(), ssid).await?;
if connected {
tracing::info!("Successfully connected to WiFi: '{}'", ssid.0);
tracing::info!("{}", t!("net.wifi.connected-successfully", ssid = &ssid.0));
} else {
tracing::info!("Failed to connect to WiFi: '{}'", ssid.0);
tracing::info!("{}", t!("net.wifi.connection-failed", ssid = &ssid.0));
match current {
None => {
tracing::info!("No WiFi to revert to!");
tracing::info!("{}", t!("net.wifi.no-wifi-to-revert"));
}
Some(current) => {
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) =
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(
color_eyre::eyre::eyre!("Can't connect to {}", ssid),
color_eyre::eyre::eyre!("{}", t!("net.wifi.cant-connect", ssid = &ssid)),
ErrorKind::Wifi,
));
}
@@ -297,7 +311,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
let wifi_manager = ctx.wifi_manager.clone();
if !ssid.is_ascii() {
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,
));
}
@@ -305,7 +319,7 @@ pub async fn remove(ctx: RpcContext, SsidParams { ssid }: SsidParams) -> Result<
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
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?;
if is_current_removed_and_no_hardwire {
return Err(Error::new(
color_eyre::eyre::eyre!(
"Forbidden: Deleting this network would make your server unreachable. Either connect to ethernet or connect to a different WiFi network to remedy this."
),
color_eyre::eyre::eyre!("{}", t!("net.wifi.forbidden-delete-would-disconnect")),
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 = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
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 = wpa_supplicant.as_ref().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
@@ -558,14 +570,14 @@ pub async fn set_country(
let wifi_manager = ctx.wifi_manager.clone();
if !interface_connected(&ctx.ethernet_interface).await? {
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,
));
}
let mut wpa_supplicant = wifi_manager.write_owned().await;
let wpa_supplicant = wpa_supplicant.as_mut().ok_or_else(|| {
Error::new(
color_eyre::eyre::eyre!("No WiFi interface available"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.no-interface-available")),
ErrorKind::Wifi,
)
})?;
@@ -684,7 +696,14 @@ impl WpaCli {
.await
.map(|_| ())
.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);
});
Command::new("nmcli")
@@ -719,13 +738,13 @@ impl WpaCli {
}
let first_country = r.lines().find(|s| s.contains("country")).ok_or_else(|| {
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,
)
})?;
let country = &RE.captures(first_country).ok_or_else(|| {
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,
)
})?[1];
@@ -734,7 +753,10 @@ impl WpaCli {
} else {
Ok(Some(CountryCode::for_alpha2(country).map_err(|_| {
Error::new(
color_eyre::eyre::eyre!("Invalid Country Code: {}", country),
color_eyre::eyre::eyre!(
"{}",
t!("net.wifi.invalid-country-code", country = country)
),
ErrorKind::Wifi,
)
})?))
@@ -877,7 +899,7 @@ impl WpaCli {
let m_id = self.check_active_network(ssid).await?;
match m_id {
None => Err(Error::new(
color_eyre::eyre::eyre!("SSID Not Found"),
color_eyre::eyre::eyre!("{}", t!("net.wifi.ssid-not-found")),
ErrorKind::Wifi,
)),
Some(x) => {
@@ -1058,7 +1080,7 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
.invoke(ErrorKind::Wifi)
.await?;
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")
.arg("reg")
.arg("set")
@@ -1066,7 +1088,7 @@ pub async fn synchronize_network_manager<P: AsRef<Path>>(
.invoke(ErrorKind::Wifi)
.await?;
} else {
tracing::info!("Setting the region fallback");
tracing::info!("{}", t!("net.wifi.setting-region-fallback"));
let _ = Command::new("iw")
.arg("reg")
.arg("set")

View File

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

View File

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

View File

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

View File

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

View File

@@ -124,7 +124,7 @@ impl RegistryContext {
};
if config.registry_hostname.is_empty() {
return Err(Error::new(
eyre!("missing required configuration: registry-hostname"),
eyre!("{}", t!("registry.context.missing-hostname")),
ErrorKind::NotFound,
));
}
@@ -195,7 +195,7 @@ impl CallRemote<RegistryContext> for CliContext {
url
} else {
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(
&self,

View File

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

View File

@@ -21,7 +21,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
from_fn_async(get_info)
.with_metadata("authenticated", Value::Bool(false))
.with_display_serializable()
.with_about("Display registry name, icon, and package categories")
.with_about("about.display-registry-info")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -29,7 +29,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
from_fn_async(set_name)
.with_metadata("admin", Value::Bool(true))
.no_display()
.with_about("Set the name for the registry")
.with_about("about.set-registry-name")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -42,7 +42,7 @@ pub fn info_api<C: Context>() -> ParentHandler<C, WithIoFormat<Empty>> {
"set-icon",
from_fn_async(cli_set_icon)
.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",
from_fn_async(get_full_index)
.with_display_serializable()
.with_about("List info including registry name and packages")
.with_about("about.list-registry-info-packages")
.with_call_remote::<CliContext>(),
)
.subcommand("info", info::info_api::<C>())
// set info and categories
.subcommand(
"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(
"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(
"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(
"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| {
if s.commitment != commitment {
Err(Error::new(
eyre!("commitment does not match"),
eyre!("{}", t!("registry.os.asset.commitment-mismatch")),
ErrorKind::InvalidSignature,
))
} else {
@@ -154,7 +154,7 @@ async fn add_asset(
})?;
Ok(())
} else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization))
Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization))
}
})
.await
@@ -208,7 +208,7 @@ pub async fn cli_add_asset(
Some("squashfs") => "squashfs",
_ => {
return Err(Error::new(
eyre!("Unknown extension"),
eyre!("{}", t!("registry.os.asset.unknown-extension")),
ErrorKind::InvalidRequest,
));
}
@@ -232,7 +232,7 @@ pub async fn cli_add_asset(
let size = file
.size()
.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 {
hash: Base64(*blake3.as_bytes()),
size,
@@ -334,7 +334,7 @@ async fn remove_asset(
.remove(&platform)?;
Ok(())
} else {
Err(Error::new(eyre!("UNAUTHORIZED"), ErrorKind::Authorization))
Err(Error::new(eyre!("{}", t!("registry.os.asset.unauthorized")), ErrorKind::Authorization))
}
})
.await

View File

@@ -34,7 +34,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"iso",
from_fn_async(cli_get_os_asset)
.no_display()
.with_about("Download iso"),
.with_about("about.download-iso"),
)
.subcommand(
"img",
@@ -46,7 +46,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"img",
from_fn_async(cli_get_os_asset)
.no_display()
.with_about("Download img"),
.with_about("about.download-img"),
)
.subcommand(
"squashfs",
@@ -58,7 +58,7 @@ pub fn get_api<C: Context>() -> ParentHandler<C> {
"squashfs",
from_fn_async(cli_get_os_asset)
.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",
from_fn_async(add::cli_add_asset)
.no_display()
.with_about("Add asset to registry"),
.with_about("about.add-asset-registry"),
)
.subcommand("remove", add::remove_api::<C>())
.subcommand("sign", sign::sign_api::<C>())
@@ -19,10 +19,10 @@ pub fn asset_api<C: Context>() -> ParentHandler<C> {
"sign",
from_fn_async(sign::cli_sign_asset)
.no_display()
.with_about("Sign file and add to registry index"),
.with_about("about.sign-file-add-registry"),
)
.subcommand(
"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)
{
return Err(Error::new(
eyre!("signer {guid} is not authorized"),
eyre!("{}", t!("registry.os.asset.signer-not-authorized", guid = guid)),
ErrorKind::Authorization,
));
}
@@ -163,7 +163,7 @@ pub async fn cli_sign_asset(
Some("squashfs") => "squashfs",
_ => {
return Err(Error::new(
eyre!("Unknown extension"),
eyre!("{}", t!("registry.os.asset.unknown-extension")),
ErrorKind::InvalidRequest,
));
}
@@ -186,7 +186,7 @@ pub async fn cli_sign_asset(
let size = file
.size()
.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 {
hash: Base64(*blake3.as_bytes()),
size,

View File

@@ -17,16 +17,16 @@ pub fn os_api<C: Context>() -> ParentHandler<C> {
from_fn_async(index::get_os_index)
.with_metadata("authenticated", Value::Bool(false))
.with_display_serializable()
.with_about("List index of OS versions")
.with_about("about.list-os-versions-index")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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(
"version",
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("get_signer", Value::Bool(true))
.no_display()
.with_about("Add OS version")
.with_about("about.add-os-version")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -35,12 +35,12 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_version)
.with_metadata("admin", Value::Bool(true))
.no_display()
.with_about("Remove OS version")
.with_about("about.remove-os-version")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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(
"get",
@@ -51,7 +51,7 @@ pub fn version_api<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, 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>(),
)
}

View File

@@ -21,7 +21,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_version_signer)
.with_metadata("admin", Value::Bool(true))
.no_display()
.with_about("Add version signer")
.with_about("about.add-version-signer")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -29,7 +29,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_version_signer)
.with_metadata("admin", Value::Bool(true))
.no_display()
.with_about("Remove version signer")
.with_about("about.remove-version-signer")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -38,7 +38,7 @@ pub fn signer_api<C: Context>() -> ParentHandler<C> {
.with_metadata("authenticated", Value::Bool(false))
.with_display_serializable()
.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>(),
)
}
@@ -95,7 +95,7 @@ pub async fn remove_version_signer(
.mutate(|s| Ok(s.remove(&signer)))?
{
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,
));
}

View File

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

View File

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

View File

@@ -441,8 +441,8 @@ pub async fn cli_download(
0 => {
return Err(Error::new(
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,
));
@@ -462,8 +462,8 @@ pub async fn cli_download(
0 => {
return Err(Error::new(
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,
));
@@ -551,7 +551,7 @@ pub async fn cli_download(
progress_tracker.complete();
progress.await.unwrap();
println!("Download Complete");
println!("{}", t!("registry.package.get.download-complete"));
Ok(())
}

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ pub fn s9pk() -> ParentHandler<CliContext> {
"pack",
from_fn_async(super::v2::pack::pack)
.no_display()
.with_about("Package s9pk input files into valid s9pk"),
.with_about("about.package-s9pk-input-files-into-valid-s9pk"),
)
.subcommand(
"list-ingredients",
@@ -45,21 +45,21 @@ pub fn s9pk() -> ParentHandler<CliContext> {
println!();
Ok(())
})
.with_about("List paths of package ingredients"),
.with_about("about.list-paths-of-package-ingredients"),
)
.subcommand(
"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(
"inspect",
inspect().with_about("Commands to display file paths, file contents, or manifest"),
inspect().with_about("about.commands-display-file-paths-contents-manifest"),
)
.subcommand(
"convert",
from_fn_async(convert)
.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)
.with_inherited(only_parent)
.no_display()
.with_about("Add image to s9pk"),
.with_about("about.add-image-to-s9pk"),
)
.subcommand(
"manifest",
from_fn_async(edit_manifest)
.with_inherited(only_parent)
.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)
.with_inherited(only_parent)
.with_display_serializable()
.with_about("Display list of paths"),
.with_about("about.display-list-of-paths"),
)
.subcommand(
"cat",
from_fn_async(cat)
.with_inherited(only_parent)
.no_display()
.with_about("Display file contents"),
.with_about("about.display-file-contents"),
)
.subcommand(
"manifest",
from_fn_async(inspect_manifest)
.with_inherited(only_parent)
.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 => {
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()?;
if matches!(&action.visibility, ActionVisibility::Disabled(_)) {
return Err(Error::new(
eyre!("action {action_id} is disabled"),
eyre!("{}", t!("service.action.action-is-disabled", action_id = action_id)),
ErrorKind::Action,
));
}
@@ -162,7 +162,7 @@ impl Handler<RunAction> for ServiceActor {
_ => false,
} {
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,
));
}

View File

@@ -1,6 +1,7 @@
use std::collections::BTreeSet;
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
use rust_i18n::t;
use crate::action::{ActionInput, ActionResult, display_action_result};
use crate::db::model::package::{
@@ -175,7 +176,7 @@ async fn run_action(
if package_id != &context.seed.id {
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,
));
context
@@ -220,7 +221,7 @@ async fn create_task(
TaskCondition::InputNotMatches => {
let Some(input) = task.input.as_ref() else {
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,
));
};
@@ -238,9 +239,7 @@ async fn create_task(
else {
return Err(Error::new(
eyre!(
"action {} of {} has no input",
task.action_id,
task.package_id
"{}", t!("service.effects.action.action-has-no-input", action_id = task.action_id, package_id = task.package_id)
),
ErrorKind::InvalidRequest,
));

View File

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

View File

@@ -28,6 +28,7 @@ use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode;
use ts_rs::TS;
use url::Url;
use crate::context::{CliContext, RpcContext};
use crate::db::model::package::{
InstalledState, ManifestPreference, PackageState, PackageStateMatchModelRef, TaskSeverity,
@@ -172,7 +173,7 @@ impl ServiceRef {
}
let service = Arc::try_unwrap(self.0).map_err(|_| {
Error::new(
eyre!("ServiceActor held somewhere after actor shutdown"),
eyre!("{}", t!("service.mod.service-actor-held-after-shutdown")),
ErrorKind::Unknown,
)
})?;
@@ -183,7 +184,7 @@ impl ServiceRef {
Arc::try_unwrap(service.seed)
.map_err(|_| {
Error::new(
eyre!("ServiceActorSeed held somewhere after actor shutdown"),
eyre!("{}", t!("service.mod.service-actor-seed-held-after-shutdown")),
ErrorKind::Unknown,
)
})?
@@ -375,7 +376,7 @@ impl Service {
{
Ok(PackageState::Installed(InstalledState { manifest }))
} 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
}
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,
)),
}
@@ -552,7 +553,7 @@ impl Service {
true
} else {
tracing::warn!(
"Deleting task {id} because action no longer exists"
"{}", t!("service.mod.deleting-task-action-no-longer-exists", id = id)
);
false
}
@@ -789,7 +790,7 @@ pub async fn attach(
.join("\n");
return Err(Error::new(
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,
));
@@ -828,7 +829,7 @@ pub async fn attach(
.map(format_subcontainer_pair)
.join("\n");
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,
));
}
@@ -985,7 +986,7 @@ pub async fn attach(
"signal" => {
if data.len() != 4 {
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
));
}
@@ -1118,14 +1119,14 @@ async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand
}
}
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,
))
}
.await
.map(RootCommand)
.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:?}");
RootCommand("/bin/sh".to_string())
})
@@ -1287,7 +1288,7 @@ pub async fn cli_attach(
"exit" => {
if data.len() != 4 {
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
));
}

View File

@@ -318,7 +318,7 @@ impl PersistentContainer {
.get()
.ok_or_else(|| {
Error::new(
eyre!("PersistentContainer has been destroyed"),
eyre!("{}", t!("service.persistent-container.container-destroyed")),
ErrorKind::Incoherent,
)
})?
@@ -354,7 +354,7 @@ impl PersistentContainer {
.get()
.ok_or_else(|| {
Error::new(
eyre!("PersistentContainer has been destroyed"),
eyre!("{}", t!("service.persistent-container.container-destroyed")),
ErrorKind::Incoherent,
)
})?
@@ -364,7 +364,7 @@ impl PersistentContainer {
let handle = NonDetachingJoinHandle::from(tokio::spawn(async move {
let chown_status = async {
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")
.arg("100000:100000")
@@ -386,7 +386,7 @@ impl PersistentContainer {
}));
let shutdown = recv.await.map_err(|_| {
Error::new(
eyre!("unix socket server thread panicked"),
eyre!("{}", t!("service.persistent-container.unix-socket-server-panicked")),
ErrorKind::Unknown,
)
})??;
@@ -396,7 +396,7 @@ impl PersistentContainer {
.is_some()
{
return Err(Error::new(
eyre!("PersistentContainer already initialized"),
eyre!("{}", t!("service.persistent-container.already-initialized")),
ErrorKind::InvalidRequest,
));
}
@@ -473,7 +473,7 @@ impl PersistentContainer {
if let Some(destroy) = self.destroy(uninit) {
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(())
}

View File

@@ -47,9 +47,9 @@ impl Actor for ServiceActor {
}
.await
{
tracing::error!("error synchronizing state of service: {e}");
tracing::error!("{}", t!("service.service-actor.error-synchronizing-state", error = 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(
Duration::from_secs(SYNC_RETRY_COOLDOWN_SECONDS),
async {

View File

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

View File

@@ -62,7 +62,7 @@ pub async fn cleanup(ctx: &RpcContext, id: &PackageId, soft: bool) -> Result<(),
| PackageState::Removing(InstalledState { manifest }) => manifest,
s => {
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,
));
}

View File

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

View File

@@ -1,3 +1,4 @@
use crate::PLATFORM;
use crate::context::RpcContext;
use crate::disk::main::export;
@@ -17,9 +18,9 @@ impl Shutdown {
use std::process::Command;
if self.restart {
tracing::info!("Beginning server restart");
tracing::info!("{}", t!("shutdown.beginning-restart"));
} else {
tracing::info!("Beginning server shutdown");
tracing::info!("{}", t!("shutdown.beginning-shutdown"));
}
let rt = tokio::runtime::Builder::new_current_thread()
@@ -35,18 +36,18 @@ impl Shutdown {
.invoke(crate::ErrorKind::Journald)
.await
{
tracing::error!("Error Stopping Journald: {}", e);
tracing::error!("{}", t!("shutdown.error-stopping-journald", error = e.to_string()));
tracing::debug!("{:?}", e);
}
if let Some(guid) = &self.disk_guid {
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);
}
}
if &*PLATFORM != "raspberrypi" || self.restart {
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);
}
}

View File

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

View File

@@ -34,7 +34,7 @@ pub fn experimental<C: Context>() -> ParentHandler<C> {
"zram",
from_fn_async(zram)
.no_display()
.with_about("Enable zram")
.with_about("about.enable-zram")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -44,7 +44,7 @@ pub fn experimental<C: Context>() -> ParentHandler<C> {
.with_custom_display_fn(|handle, 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>(),
)
}
@@ -159,7 +159,10 @@ pub async fn governor(
if let Some(set) = set {
if !available.contains(&set) {
return Err(Error::new(
eyre!("Governor {set} not available"),
eyre!(
"{}",
t!("system.governor-not-available", governor = set.to_string())
),
ErrorKind::InvalidRequest,
));
}
@@ -295,7 +298,7 @@ pub fn kiosk<C: Context>() -> ParentHandler<C> {
enable_kiosk().await
})
.no_display()
.with_about("Enable kiosk mode")
.with_about("about.enable-kiosk-mode")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -313,7 +316,7 @@ pub fn kiosk<C: Context>() -> ParentHandler<C> {
disable_kiosk().await
})
.no_display()
.with_about("Disable kiosk mode")
.with_about("about.disable-kiosk-mode")
.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 {
Ok(a) => Some(a),
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);
None
}
@@ -570,12 +579,24 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break;
}
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);
}
},
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);
}
}
@@ -590,7 +611,13 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break;
}
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);
}
}
@@ -605,7 +632,13 @@ pub async fn launch_metrics_task<F: FnMut() -> Receiver<Option<Shutdown>>>(
break;
}
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);
}
}
@@ -650,7 +683,13 @@ async fn launch_temp_task(
});
}
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);
}
}
@@ -673,7 +712,13 @@ async fn launch_cpu_task(
cache.send_modify(|c| c.as_mut().unwrap().cpu = info);
}
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);
}
}
@@ -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);
}
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);
}
}
@@ -721,7 +772,13 @@ async fn launch_disk_task(
});
}
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);
}
}
@@ -757,7 +814,12 @@ async fn get_temp() -> Result<Celsius, Error> {
}
})
.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))
}
@@ -803,7 +865,10 @@ async fn get_proc_stat() -> Result<ProcStat, Error> {
.map(|s| {
s.parse::<u64>().map_err(|e| {
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,
)
})
@@ -813,8 +878,8 @@ async fn get_proc_stat() -> Result<ProcStat, Error> {
if stats.len() < 10 {
Err(Error::new(
eyre!(
"Columns missing from /proc/stat. Need 10, found {}",
stats.len()
"{}",
t!("system.columns-missing-from-proc-stat", count = stats.len())
),
ErrorKind::ParseSysInfo,
))
@@ -872,16 +937,22 @@ pub async fn get_mem_info() -> Result<MetricsMemory, Error> {
zram_free: None,
};
fn get_num_kb(l: &str) -> Result<u64, Error> {
let e = Error::new(
color_eyre::eyre::eyre!("Invalid meminfo line: {}", l),
ErrorKind::ParseSysInfo,
);
let line = l.to_string();
let e = || {
Error::new(
color_eyre::eyre::eyre!(
"{}",
t!("system.invalid-meminfo-line", line = line.clone())
),
ErrorKind::ParseSysInfo,
)
};
match l.split_whitespace().skip(1).next() {
Some(x) => match x.parse() {
Ok(y) => Ok(y),
Err(_) => Err(e),
Err(_) => Err(e()),
},
None => Err(e),
None => Err(e()),
}
}
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> {
a.ok_or(Error::new(
color_eyre::eyre::eyre!("{} missing from /proc/meminfo", field),
ErrorKind::ParseSysInfo,
))
let field_str = field.to_string();
a.ok_or_else(|| {
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_free = ensure_present(mem_info.mem_free, "MemFree")?;
@@ -1136,7 +1216,7 @@ pub async fn set_language(
.await?;
write_file_atomic(
"/media/startos/config/overlay/etc/default/locale",
format!("{language}.UTF-8\n").as_bytes(),
format!("LANG={language}.UTF-8\n").as_bytes(),
)
.await?;
ctx.db

View File

@@ -20,19 +20,19 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
.subcommand(
"db",
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(
"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(
"subnet",
subnet_api::<C>().with_about("Add, remove, or modify subnets"),
subnet_api::<C>().with_about("about.add-remove-or-modify-subnets"),
)
.subcommand(
"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(
"port-forward",
@@ -42,7 +42,7 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_forward)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Add a new port forward")
.with_about("about.add-new-port-forward")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -50,7 +50,7 @@ pub fn tunnel_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_forward)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Remove a port forward")
.with_about("about.remove-port-forward")
.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_inherited(|a, _| a)
.no_display()
.with_about("Add a new subnet")
.with_about("about.add-new-subnet")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -79,7 +79,7 @@ pub fn subnet_api<C: Context>() -> ParentHandler<C, SubnetParams> {
.with_metadata("sync_db", Value::Bool(true))
.with_inherited(|a, _| a)
.no_display()
.with_about("Remove a subnet")
.with_about("about.remove-subnet")
.with_call_remote::<CliContext>(),
)
}
@@ -91,7 +91,7 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
from_fn_async(add_device)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Add a device to a subnet")
.with_about("about.add-device-to-subnet")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -99,7 +99,7 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
from_fn_async(remove_device)
.with_metadata("sync_db", Value::Bool(true))
.no_display()
.with_about("Remove a device from a subnet")
.with_about("about.remove-device-from-subnet")
.with_call_remote::<CliContext>(),
)
.subcommand(
@@ -123,13 +123,13 @@ pub fn device_api<C: Context>() -> ParentHandler<C> {
Ok(())
})
.with_about("List devices in a subnet")
.with_about("about.list-devices-in-subnet")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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>(),
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,7 +67,7 @@ pub async fn get_available_governors() -> Result<BTreeSet<Governor>, Error> {
for_cpu
.entry(current_cpu.ok_or_else(|| {
Error::new(
eyre!("governors listed before cpu"),
eyre!("{}", t!("util.cpupower.governors-listed-before-cpu")),
ErrorKind::ParseSysInfo,
)
})?)
@@ -95,6 +95,7 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
let Some(raw) = Command::new("cpupower")
.arg("frequency-info")
.arg("-p")
.env("LANG", "C.UTF-8")
.invoke(ErrorKind::CpuSettings)
.await
.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(
eyre!("Failed to parse cpupower output:\n{raw}"),
eyre!(
"{}",
t!("util.cpupower.failed-to-parse-output", output = raw)
),
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)]
#[ts(type = "string")]
pub enum PathOrUrl {

View File

@@ -97,7 +97,7 @@ impl WebSocket {
if self.ping_state.is_some() {
self.fused = true;
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()));

View File

@@ -17,7 +17,7 @@ use crate::util::{Apply, PathOrUrl};
pub fn util<C: Context>() -> ParentHandler<C> {
ParentHandler::new().subcommand(
"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
} else {
Err(Error::new(
eyre!("unknown scheme: {}", url.scheme()),
eyre!("{}", t!("util.rpc.unknown-scheme", scheme = url.scheme())),
ErrorKind::InvalidRequest,
))
}

View File

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

View File

@@ -10,7 +10,7 @@ fn map_miette(m: miette::Error) -> Error {
}
fn noninteractive_err() -> Error {
Error::new(
eyre!("Terminal must be in interactive mode for this wizard"),
eyre!("{}", t!("util.tui.terminal-must-be-interactive")),
ErrorKind::Filesystem,
)
}
@@ -21,7 +21,7 @@ where
{
move |s| {
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 => {
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}")?,
},
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
.map_err(map_miette)?;
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
.iter()
@@ -127,7 +127,7 @@ pub async fn choose_custom_display<'t, T>(
.find(|(_, s)| s.as_str() == choice[0].as_str())
.ok_or_else(|| {
Error::new(
eyre!("selected choice does not appear in input"),
eyre!("{}", t!("util.tui.selected-choice-not-in-input")),
ErrorKind::Incoherent,
)
})?;