Feature/consolidate setup (#3092)

* start consolidating

* add start-cli flash-os

* combine install and setup and refactor all

* use http

* undo mock

* fix translation

* translations

* use dialogservice wrapper

* better ST messaging on setup

* only warn on update if breakages (#3097)

* finish setup wizard and ui language-keyboard feature

* fix typo

* wip: localization

* remove start-tunnel readme

* switch to posix strings for language internal

* revert mock

* translate backend strings

* fix missing about text

* help text for args

* feat: add "Add new gateway" option (#3098)

* feat: add "Add new gateway" option

* Update web/projects/ui/src/app/routes/portal/components/form/controls/select.component.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add translation

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Matt Hill <mattnine@protonmail.com>

* fix dns selection

* keyboard keymap also

* ability to shutdown after install

* revert mock

* working setup flow + manifest localization

* (mostly) redundant localization on frontend

* version bump

* omit live medium from disk list and better space management

* ignore missing package archive on 035 migration

* fix device migration

* add i18n helper to sdk

* fix install over 0.3.5.1

* fix grub config

---------

Co-authored-by: Matt Hill <mattnine@protonmail.com>
Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
Co-authored-by: Alex Inkin <alexander@inkin.ru>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Aiden McClelland
2026-01-27 14:44:41 -08:00
committed by GitHub
parent 99871805bd
commit c65db31fd9
251 changed files with 12163 additions and 3966 deletions

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,14 +129,14 @@ pub fn auth_api<C: Context>() -> ParentHandler<C> {
.subcommand(
"set-password",
from_fn_async(set_password_cli)
.with_about("Set user interface password")
.no_display(),
.no_display()
.with_about("about.set-user-interface-password"),
)
.subcommand(
"reset-password",
from_fn_async(reset_password)
.with_about("Reset user interface password")
.no_display(),
.no_display()
.with_about("about.reset-user-interface-password"),
)
.subcommand(
"key",
@@ -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

@@ -43,11 +43,11 @@ use crate::util::sync::{SyncMutex, Watch};
#[serde(rename_all = "kebab-case")]
#[command(rename_all = "kebab-case")]
pub struct TunnelConfig {
#[arg(short = 'c', long = "config")]
#[arg(short = 'c', long = "config", help = "help.arg.config-file-path")]
pub config: Option<PathBuf>,
#[arg(short = 'l', long = "listen")]
#[arg(short = 'l', long = "listen", help = "help.arg.tunnel-listen-address")]
pub tunnel_listen: Option<SocketAddr>,
#[arg(short = 'd', long = "datadir")]
#[arg(short = 'd', long = "datadir", help = "help.arg.data-directory")]
pub datadir: Option<PathBuf>,
}
impl ContextConfig for TunnelConfig {
@@ -244,6 +244,7 @@ impl Deref for TunnelContext {
#[derive(Debug, Deserialize, Serialize, Parser)]
pub struct TunnelAddrParams {
#[arg(help = "help.arg.tunnel-ip-address")]
pub tunnel: IpAddr,
}
@@ -310,6 +311,7 @@ impl CallRemote<TunnelContext> for CliContext {
#[derive(Debug, Deserialize, Serialize, Parser)]
pub struct TunnelUrlParams {
#[arg(help = "help.arg.tunnel-url")]
pub tunnel: Url,
}

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",
@@ -121,8 +121,9 @@ pub fn db_api<C: Context>() -> ParentHandler<C> {
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
pub struct CliDumpParams {
#[arg(long = "pointer", short = 'p')]
#[arg(long = "pointer", short = 'p', help = "help.arg.json-pointer")]
pointer: Option<JsonPointer>,
#[arg(help = "help.arg.database-path")]
path: Option<PathBuf>,
}
@@ -154,7 +155,7 @@ async fn cli_dump(
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
pub struct DumpParams {
#[arg(long = "pointer", short = 'p')]
#[arg(long = "pointer", short = 'p', help = "help.arg.json-pointer")]
#[ts(type = "string | null")]
pointer: Option<JsonPointer>,
}
@@ -170,7 +171,9 @@ pub async fn dump(ctx: TunnelContext, DumpParams { pointer }: DumpParams) -> Res
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
pub struct CliApplyParams {
#[arg(help = "help.arg.db-apply-expr")]
expr: String,
#[arg(help = "help.arg.database-path")]
path: Option<PathBuf>,
}
@@ -225,7 +228,9 @@ async fn cli_apply(
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
pub struct ApplyParams {
#[arg(help = "help.arg.db-apply-expr")]
expr: String,
#[arg(help = "help.arg.database-path")]
path: Option<PathBuf>,
}

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,28 +150,28 @@ 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")
.no_display()
.with_about("about.enable-webserver")
.with_call_remote::<CliContext>(),
)
.subcommand(
"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>(),
)
}
@@ -279,7 +279,7 @@ pub async fn import_certificate_cli(
#[derive(Debug, Deserialize, Serialize, Parser)]
pub struct GenerateCertParams {
#[arg(help = "Subject Alternative Name(s)")]
#[arg(help = "help.arg.cert-subject-alt-names")]
pub subject: Vec<InternedString>,
}
@@ -331,6 +331,7 @@ pub async fn get_certificate(ctx: TunnelContext) -> Result<Option<Pem<Vec<X509>>
#[derive(Debug, Deserialize, Serialize, Parser)]
#[serde(rename_all = "camelCase")]
pub struct SetListenParams {
#[arg(help = "help.arg.listen-address")]
pub listen: SocketAddr,
}
@@ -466,7 +467,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
println!("✅ Success! ✅");
println!(
"The webserver is running. Below is your URL{} and Root Certificate Authority (Root CA).",
"StartTunnel installed successfully. Below is your Web URL{} and Root Certificate Authority (Root CA).",
if password.is_some() {
", password,"
} else {
@@ -474,7 +475,7 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
}
);
println!();
println!("🌐 URL");
println!("🌐 Web URL");
println!("https://{listen}");
if listen.ip().is_unspecified() {
println!(concat!(
@@ -517,21 +518,32 @@ pub async fn init_web(ctx: CliContext) -> Result<(), Error> {
.map(Pem)
.or_not_found("certificate in chain")?;
println!("📝 Root CA:");
print!("{cert}");
print!("{cert}\n");
println!(concat!(
"To trust your StartTunnel Root CA (above):\n",
" 1. Copy the Root CA ",
"(starting with -----BEGIN CERTIFICATE----- and ending with -----END CERTIFICATE-----).\n",
" 2. Open a text editor: \n",
" - Linux: gedit, nano, or any editor\n",
" - Mac: TextEdit\n",
" - Windows: Notepad\n",
" 3. Paste the contents of your Root CA.\n",
" 4. Save the file with a `.crt` extension ",
"(e.g. `start-tunnel.crt`) (make sure it saves as plain text, not rich text).\n",
" 5. Follow instructions to trust you StartTunnel Root CA: ",
"https://staging.docs.start9.com/user-manual/trust-ca.html#2-trust-your-servers-root-ca."
"To access your Web URL securely, trust your Root CA (displayed above) on your client device(s):\n",
" - MacOS\n",
" 1. Open the Terminal app\n",
" 2. Paste the following command (**DO NOTt** click Return): pbcopy < ~/Desktop/ca.crt\n",
" 3. Copy your Root CA (including -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----)\n",
" 4. Back in Terminal, click Return. ca.crt is saved to your Desktop\n",
" 5. Complete by trusting your Root CA: https://https://staging.docs.start9.com/device-guides/mac/ca.html\n",
" - Linux\n",
" 1. Open gedit, nano, or any editor\n",
" 2. Copy/paste your Root CA (including -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----)\n",
" 3. Name the file ca.crt and save as plaintext\n",
" 5. Complete by trusting your Root CA: https://https://staging.docs.start9.com/device-guides/linux/ca.html\n",
" - Windows\n",
" 1. Open the Notepad app\n",
" 2. Copy/paste your Root CA (including -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----)\n",
" 3. Name the file ca.crt and save as plaintext\n",
" 5. Complete by trusting your Root CA: https://https://staging.docs.start9.com/device-guides/windows/ca.html\n",
" - Android/Graphene\n",
" 1. Send the ca.crt file (created above) to yourself\n",
" 2. Complete by trusting your Root CA: https://https://staging.docs.start9.com/device-guides/android/ca.html\n",
" - iOS\n",
" 1. Send the ca.crt file (created above) to yourself\n",
" 2. Complete by trusting your Root CA: https://https://staging.docs.start9.com/device-guides/ios/ca.html\n",
));
return Ok(());

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| {