mirror of https://github.com/ospab/ostp.git
Refactor CLI to subcommands, fix UAC elevation, and add multi-channel release
This commit is contained in:
parent
80b4ad8d54
commit
5c2b5a06dd
|
|
@ -6,6 +6,9 @@ on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- pre-release
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
@ -227,9 +230,10 @@ jobs:
|
||||||
|
|
||||||
# ── Upload ─────────────────────────────────────────────────────────────
|
# ── Upload ─────────────────────────────────────────────────────────────
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
tag_name: ${{ github.ref_name == 'master' && 'nightly' || (github.ref_name == 'pre-release' && 'pre-release' || github.ref_name) }}
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
files: ${{ matrix.release_name }}
|
files: ${{ matrix.release_name }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -305,9 +309,10 @@ jobs:
|
||||||
Compress-Archive -Path "$dir/*" -DestinationPath "ostp-windows-gui-${{ matrix.arch }}.zip" -Force
|
Compress-Archive -Path "$dir/*" -DestinationPath "ostp-windows-gui-${{ matrix.arch }}.zip" -Force
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
tag_name: ${{ github.ref_name == 'master' && 'nightly' || (github.ref_name == 'pre-release' && 'pre-release' || github.ref_name) }}
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
files: ostp-windows-gui-${{ matrix.arch }}.zip
|
files: ostp-windows-gui-${{ matrix.arch }}.zip
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -369,9 +374,10 @@ jobs:
|
||||||
tar -czf ostp-linux-gui-${{ matrix.arch }}.tar.gz ostp-linux-gui-${{ matrix.arch }}
|
tar -czf ostp-linux-gui-${{ matrix.arch }}.tar.gz ostp-linux-gui-${{ matrix.arch }}
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
tag_name: ${{ github.ref_name == 'master' && 'nightly' || (github.ref_name == 'pre-release' && 'pre-release' || github.ref_name) }}
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
files: ostp-linux-gui-${{ matrix.arch }}.tar.gz
|
files: ostp-linux-gui-${{ matrix.arch }}.tar.gz
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -430,9 +436,10 @@ jobs:
|
||||||
tar -czf ostp-macos-gui-${{ matrix.arch }}.tar.gz ostp-macos-gui-${{ matrix.arch }}
|
tar -czf ostp-macos-gui-${{ matrix.arch }}.tar.gz ostp-macos-gui-${{ matrix.arch }}
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
tag_name: ${{ github.ref_name == 'master' && 'nightly' || (github.ref_name == 'pre-release' && 'pre-release' || github.ref_name) }}
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
files: ostp-macos-gui-${{ matrix.arch }}.tar.gz
|
files: ostp-macos-gui-${{ matrix.arch }}.tar.gz
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -503,9 +510,10 @@ jobs:
|
||||||
cp build/app/outputs/flutter-apk/app-release.apk ostp-android-${{ matrix.arch }}.apk
|
cp build/app/outputs/flutter-apk/app-release.apk ostp-android-${{ matrix.arch }}.apk
|
||||||
|
|
||||||
- name: Upload to GitHub Release
|
- name: Upload to GitHub Release
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
|
tag_name: ${{ github.ref_name == 'master' && 'nightly' || (github.ref_name == 'pre-release' && 'pre-release' || github.ref_name) }}
|
||||||
|
prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
files: ostp-flutter/ostp-android-${{ matrix.arch }}.apk
|
files: ostp-flutter/ostp-android-${{ matrix.arch }}.apk
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
||||||
|
|
@ -741,9 +741,9 @@ fn launch_as_admin(exe: &std::path::PathBuf, token: &str, port: u16) -> anyhow::
|
||||||
let cwd_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("."));
|
let cwd_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("."));
|
||||||
let dir_wstr: Vec<u16> = cwd_path.parent().unwrap_or(std::path::Path::new(".")).as_os_str().encode_wide().chain(Some(0)).collect();
|
let dir_wstr: Vec<u16> = cwd_path.parent().unwrap_or(std::path::Path::new(".")).as_os_str().encode_wide().chain(Some(0)).collect();
|
||||||
|
|
||||||
let ret = unsafe { ShellExecuteW(null_mut(), verb_wstr.as_ptr(), exe_wstr.as_ptr(), params_wstr.as_ptr(), dir_wstr.as_ptr(), 0) };
|
let ret = unsafe { ShellExecuteW(null_mut(), verb_wstr.as_ptr(), exe_wstr.as_ptr(), params_wstr.as_ptr(), dir_wstr.as_ptr(), 1) };
|
||||||
|
|
||||||
if ret <= 32 { anyhow::bail!("UAC denied or helper missing."); }
|
if ret <= 32 { anyhow::bail!("UAC denied or helper missing (ret={}).", ret); }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
296
ostp/src/main.rs
296
ostp/src/main.rs
|
|
@ -11,75 +11,93 @@ mod dns_prober;
|
||||||
#[command(author, version, about = "OSTP Core - Ospab Stealth Transport Protocol", long_about = None)]
|
#[command(author, version, about = "OSTP Core - Ospab Stealth Transport Protocol", long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Path to the JSON configuration file
|
/// Path to the JSON configuration file
|
||||||
#[cfg_attr(unix, arg(long, default_value = "/etc/ostp/config.json", help_heading = "Common Commands"))]
|
#[cfg_attr(unix, arg(short, long, default_value = "/etc/ostp/config.json", global = true))]
|
||||||
#[cfg_attr(windows, arg(long, default_value = "config.json", help_heading = "Common Commands"))]
|
#[cfg_attr(windows, arg(short, long, default_value = "config.json", global = true))]
|
||||||
config: PathBuf,
|
config: PathBuf,
|
||||||
|
|
||||||
/// Optional mode to initialize the config for (client or server)
|
#[command(subcommand)]
|
||||||
#[arg(short, long, help_heading = "Common Commands")]
|
command: Option<Commands>,
|
||||||
init: Option<String>,
|
}
|
||||||
|
|
||||||
|
#[derive(clap::Subcommand, Debug)]
|
||||||
|
enum Commands {
|
||||||
/// Run the interactive setup wizard
|
/// Run the interactive setup wizard
|
||||||
#[arg(long, help_heading = "Common Commands")]
|
Setup {
|
||||||
|
/// Optional mode to initialize the config for (client or server)
|
||||||
|
#[arg(short, long)]
|
||||||
|
init: Option<String>,
|
||||||
|
},
|
||||||
|
/// Initialize config
|
||||||
|
Init {
|
||||||
|
mode: String,
|
||||||
|
},
|
||||||
|
/// Generate a new secure access key
|
||||||
|
GenerateKey {
|
||||||
|
/// Format for generated key (hex, base64)
|
||||||
|
#[arg(long, default_value = "hex")]
|
||||||
|
format: String,
|
||||||
|
/// Number of keys to generate
|
||||||
|
#[arg(short = 'c', long, default_value_t = 1)]
|
||||||
|
count: usize,
|
||||||
|
},
|
||||||
|
/// Output ready-to-use client sharing links
|
||||||
|
Links,
|
||||||
|
/// Validate configuration file
|
||||||
|
Check,
|
||||||
|
/// Connect using a share link
|
||||||
|
Connect {
|
||||||
|
url: String,
|
||||||
|
},
|
||||||
|
/// Uninstall OSTP
|
||||||
|
Uninstall,
|
||||||
|
/// Update OSTP
|
||||||
|
Update {
|
||||||
|
/// Specify the release branch to update from (stable, pre-release, nightly)
|
||||||
|
#[arg(short, long, default_value = "stable")]
|
||||||
|
branch: String,
|
||||||
|
|
||||||
|
/// Specify a specific target version to update to (e.g., 0.2.98)
|
||||||
|
#[arg(short, long)]
|
||||||
|
version: Option<String>,
|
||||||
|
},
|
||||||
|
/// Import a share link
|
||||||
|
Import {
|
||||||
|
url: String,
|
||||||
|
},
|
||||||
|
/// Output shell export commands for proxy
|
||||||
|
ProxyEnv,
|
||||||
|
/// Output shell export commands to clear proxy
|
||||||
|
ProxyEnvClear,
|
||||||
|
/// Force migration of the configuration file
|
||||||
|
Migrate,
|
||||||
|
/// Run the network prober
|
||||||
|
Prober,
|
||||||
|
/// Run daemon
|
||||||
|
Run,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LegacyArgs {
|
||||||
|
config: PathBuf,
|
||||||
|
init: Option<String>,
|
||||||
setup: bool,
|
setup: bool,
|
||||||
|
|
||||||
/// Generate a new secure access key and exit
|
|
||||||
#[arg(short = 'g', long, help_heading = "Common Commands")]
|
|
||||||
generate_key: bool,
|
generate_key: bool,
|
||||||
|
|
||||||
/// Format for generated key (hex, base64)
|
|
||||||
#[arg(long, default_value = "hex", help_heading = "Common Commands")]
|
|
||||||
format: String,
|
format: String,
|
||||||
|
|
||||||
/// Number of keys to generate
|
|
||||||
#[arg(short = 'c', long, default_value_t = 1, help_heading = "Common Commands")]
|
|
||||||
count: usize,
|
count: usize,
|
||||||
|
|
||||||
/// Output ready-to-use client sharing links (ostp://...) from the server configuration
|
|
||||||
#[arg(long, help_heading = "Server Commands")]
|
|
||||||
links: bool,
|
links: bool,
|
||||||
|
|
||||||
/// Validate configuration file and exit
|
|
||||||
#[arg(long, help_heading = "Common Commands")]
|
|
||||||
check: bool,
|
check: bool,
|
||||||
|
|
||||||
/// Optional client connection share link (ostp://ACCESS_KEY@HOST:PORT) to run instantly
|
|
||||||
#[arg(help_heading = "Client Commands")]
|
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
|
|
||||||
/// Uninstall OSTP: stop service, remove binary and configuration files
|
|
||||||
#[arg(long, help_heading = "Common Commands")]
|
|
||||||
uninstall: bool,
|
uninstall: bool,
|
||||||
|
|
||||||
/// Update OSTP: re-run the install script to fetch and install the latest version
|
|
||||||
#[arg(long, help_heading = "Common Commands")]
|
|
||||||
update: bool,
|
update: bool,
|
||||||
|
update_branch: String,
|
||||||
/// Specify a target version for the update command (e.g., -v 0.2.98)
|
|
||||||
#[arg(short = 'v', long = "target-version", help_heading = "Common Commands")]
|
|
||||||
target_version: Option<String>,
|
target_version: Option<String>,
|
||||||
|
|
||||||
/// Import a share link (ostp://...) into the configuration file and exit
|
|
||||||
#[arg(long, help_heading = "Client Commands")]
|
|
||||||
import: Option<String>,
|
import: Option<String>,
|
||||||
|
|
||||||
/// Output shell export commands for proxy (eval $(ostp --proxy-env))
|
|
||||||
#[arg(long, help_heading = "Client Commands")]
|
|
||||||
proxy_env: bool,
|
proxy_env: bool,
|
||||||
|
|
||||||
/// Output shell export commands to clear proxy (eval $(ostp --proxy-env-clear))
|
|
||||||
#[arg(long, help_heading = "Client Commands")]
|
|
||||||
proxy_env_clear: bool,
|
proxy_env_clear: bool,
|
||||||
|
|
||||||
/// Force migration of the configuration file to the latest format and exit
|
|
||||||
#[arg(long, help_heading = "Common Commands")]
|
|
||||||
migrate: bool,
|
migrate: bool,
|
||||||
|
|
||||||
/// Run the network prober to find the fastest DNS resolver for the DNS Transport
|
|
||||||
#[arg(long, help_heading = "Client Commands")]
|
|
||||||
prober: bool,
|
prober: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn patch_existing_client_config(config_path: &std::path::Path, new_client_inner: serde_json::Value) -> serde_json::Value {
|
fn patch_existing_client_config(config_path: &std::path::Path, new_client_inner: serde_json::Value) -> serde_json::Value {
|
||||||
let unified_new = serde_json::to_value(UnifiedConfig {
|
let unified_new = serde_json::to_value(UnifiedConfig {
|
||||||
mode: AppMode::Client(new_client_inner.clone()),
|
mode: AppMode::Client(new_client_inner.clone()),
|
||||||
|
|
@ -465,7 +483,7 @@ struct FallbackCfg {
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let _ = rlimit::increase_nofile_limit(1048576);
|
let _ = rlimit::increase_nofile_limit(1048576);
|
||||||
ostp_client::logging::setup_panic_hook();
|
ostp_client::logging::setup_panic_hook();
|
||||||
let _log_guard = ostp_client::logging::init_tracing("info", "ostp-cli", env!("CARGO_PKG_VERSION"));
|
// Tracing init moved to run_app
|
||||||
|
|
||||||
let res = run_app().await;
|
let res = run_app().await;
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
|
@ -1192,15 +1210,120 @@ fn wizard_register_windows_service(config_path: &std::path::Path) -> Result<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_interactive_prompts(client_cfg: &mut serde_json::Value) {
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
print!("{} Enable TUN (VPN) mode? [y/N]: ", "?".blue().bold());
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if input.trim().eq_ignore_ascii_case("y") {
|
||||||
|
if let Some(i_val) = client_cfg.get_mut("inbounds") {
|
||||||
|
if let Some(inbounds) = i_val.as_array_mut() {
|
||||||
|
for inbound in inbounds.iter_mut() {
|
||||||
|
if inbound.get("type").and_then(|t| t.as_str()) == Some("tun") {
|
||||||
|
inbound["auto_route"] = serde_json::json!(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("{} Enable connection multiplexing (mux)? [y/N]: ", "?".blue().bold());
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
input.clear();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if input.trim().eq_ignore_ascii_case("y") {
|
||||||
|
print!("How many sessions? [5]: ");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
input.clear();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let mut sessions = 5;
|
||||||
|
if !input.trim().is_empty() {
|
||||||
|
if let Ok(s) = input.trim().parse() {
|
||||||
|
sessions = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(o_val) = client_cfg.get_mut("outbounds") {
|
||||||
|
if let Some(outbounds) = o_val.as_array_mut() {
|
||||||
|
for outbound in outbounds.iter_mut() {
|
||||||
|
if outbound.get("type").and_then(|t| t.as_str()) == Some("ostp") {
|
||||||
|
outbound["multiplex"] = serde_json::json!({
|
||||||
|
"enabled": true,
|
||||||
|
"sessions": sessions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("Enable debug mode? [y/N]: ");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
input.clear();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
if input.trim().eq_ignore_ascii_case("y") {
|
||||||
|
client_cfg["log"]["level"] = serde_json::json!("debug");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn run_app() -> Result<()> {
|
async fn run_app() -> Result<()> {
|
||||||
let args = Args::parse();
|
let raw_args = Args::parse();
|
||||||
|
let mut args = LegacyArgs {
|
||||||
|
config: raw_args.config.clone(),
|
||||||
|
init: None,
|
||||||
|
setup: false,
|
||||||
|
generate_key: false,
|
||||||
|
format: "hex".to_string(),
|
||||||
|
count: 1,
|
||||||
|
links: false,
|
||||||
|
check: false,
|
||||||
|
url: None,
|
||||||
|
uninstall: false,
|
||||||
|
update: false,
|
||||||
|
update_branch: "stable".to_string(),
|
||||||
|
target_version: None,
|
||||||
|
import: None,
|
||||||
|
proxy_env: false,
|
||||||
|
proxy_env_clear: false,
|
||||||
|
migrate: false,
|
||||||
|
prober: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut enable_logging = true;
|
||||||
|
|
||||||
|
if let Some(cmd) = &raw_args.command {
|
||||||
|
match cmd {
|
||||||
|
Commands::Setup { init } => { args.setup = true; args.init = init.clone(); }
|
||||||
|
Commands::Init { mode } => { args.init = Some(mode.clone()); enable_logging = false; }
|
||||||
|
Commands::GenerateKey { format, count } => { args.generate_key = true; args.format = format.clone(); args.count = *count; enable_logging = false; }
|
||||||
|
Commands::Links => { args.links = true; enable_logging = false; }
|
||||||
|
Commands::Check => { args.check = true; enable_logging = false; }
|
||||||
|
Commands::Connect { url } => { args.url = Some(url.clone()); }
|
||||||
|
Commands::Uninstall => { args.uninstall = true; enable_logging = false; }
|
||||||
|
Commands::Update { branch, version } => { args.update = true; args.update_branch = branch.clone(); args.target_version = version.clone(); enable_logging = false; }
|
||||||
|
Commands::Import { url } => { args.import = Some(url.clone()); enable_logging = false; }
|
||||||
|
Commands::ProxyEnv => { args.proxy_env = true; enable_logging = false; }
|
||||||
|
Commands::ProxyEnvClear => { args.proxy_env_clear = true; enable_logging = false; }
|
||||||
|
Commands::Migrate => { args.migrate = true; enable_logging = false; }
|
||||||
|
Commands::Prober => { args.prober = true; enable_logging = false; }
|
||||||
|
Commands::Run => { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _log_guard = if enable_logging {
|
||||||
|
Some(ostp_client::logging::init_tracing("info", "ostp-cli", env!("CARGO_PKG_VERSION")))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
if args.uninstall {
|
if args.uninstall {
|
||||||
return cmd_uninstall();
|
return cmd_uninstall();
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.update {
|
if args.update {
|
||||||
return cmd_update(args.target_version);
|
return cmd_update(args.update_branch, args.target_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.migrate {
|
if args.migrate {
|
||||||
|
|
@ -1300,8 +1423,11 @@ async fn run_app() -> Result<()> {
|
||||||
|
|
||||||
if let Some(import_url) = args.import {
|
if let Some(import_url) = args.import {
|
||||||
println!("{} Importing configuration from share link...", "[ostp]".cyan().bold());
|
println!("{} Importing configuration from share link...", "[ostp]".cyan().bold());
|
||||||
let client_cfg = parse_ostp_link(&import_url)
|
let mut client_cfg = parse_ostp_link(&import_url)
|
||||||
.map_err(|e| anyhow!("Share Link Error: {e}"))?;
|
.map_err(|e| anyhow!("Share Link Error: {e}"))?;
|
||||||
|
|
||||||
|
apply_interactive_prompts(&mut client_cfg);
|
||||||
|
|
||||||
let patched = patch_existing_client_config(&args.config, client_cfg);
|
let patched = patch_existing_client_config(&args.config, client_cfg);
|
||||||
let content = serde_json::to_string_pretty(&patched)?;
|
let content = serde_json::to_string_pretty(&patched)?;
|
||||||
if let Some(parent) = args.config.parent() {
|
if let Some(parent) = args.config.parent() {
|
||||||
|
|
@ -1319,61 +1445,7 @@ async fn run_app() -> Result<()> {
|
||||||
let mut client_cfg = parse_ostp_link(&url)
|
let mut client_cfg = parse_ostp_link(&url)
|
||||||
.map_err(|e| anyhow!("Share Link Error: {e}"))?;
|
.map_err(|e| anyhow!("Share Link Error: {e}"))?;
|
||||||
|
|
||||||
// Interactive prompt for URL launch
|
apply_interactive_prompts(&mut client_cfg);
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
print!("{} Enable TUN (VPN) mode? [y/N]: ", "?".blue().bold());
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
let mut input = String::new();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if input.trim().eq_ignore_ascii_case("y") {
|
|
||||||
if let Some(i_val) = client_cfg.get_mut("inbounds") {
|
|
||||||
if let Some(inbounds) = i_val.as_array_mut() {
|
|
||||||
for inbound in inbounds.iter_mut() {
|
|
||||||
if inbound.get("type").and_then(|t| t.as_str()) == Some("tun") {
|
|
||||||
inbound["auto_route"] = serde_json::json!(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print!("{} Enable connection multiplexing (mux)? [y/N]: ", "?".blue().bold());
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
input.clear();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if input.trim().eq_ignore_ascii_case("y") {
|
|
||||||
print!("How many sessions? [5]: ");
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
input.clear();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
let mut sessions = 5;
|
|
||||||
if !input.trim().is_empty() {
|
|
||||||
if let Ok(s) = input.trim().parse() {
|
|
||||||
sessions = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(o_val) = client_cfg.get_mut("outbounds") {
|
|
||||||
if let Some(outbounds) = o_val.as_array_mut() {
|
|
||||||
for outbound in outbounds.iter_mut() {
|
|
||||||
if outbound.get("type").and_then(|t| t.as_str()) == Some("ostp") {
|
|
||||||
outbound["multiplex"] = serde_json::json!({
|
|
||||||
"enabled": true,
|
|
||||||
"sessions": sessions
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print!("Enable debug mode? [y/N]: ");
|
|
||||||
std::io::stdout().flush().unwrap();
|
|
||||||
input.clear();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
if input.trim().eq_ignore_ascii_case("y") {
|
|
||||||
client_cfg["log"]["level"] = serde_json::json!("debug");
|
|
||||||
}
|
|
||||||
|
|
||||||
let patched = patch_existing_client_config(&args.config, client_cfg);
|
let patched = patch_existing_client_config(&args.config, client_cfg);
|
||||||
return run_client_directly(patched).await;
|
return run_client_directly(patched).await;
|
||||||
|
|
@ -1986,16 +2058,16 @@ fn cmd_uninstall() -> Result<()> {
|
||||||
// Update command
|
// Update command
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn cmd_update(version: Option<String>) -> Result<()> {
|
fn cmd_update(branch: String, version: Option<String>) -> Result<()> {
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
println!("[ostp] Updating OSTP...");
|
println!("[ostp] Updating OSTP...");
|
||||||
|
|
||||||
let mut script_args = vec!["-c".to_string()];
|
let mut script_args = vec!["-c".to_string()];
|
||||||
if let Some(v) = version {
|
if let Some(v) = version {
|
||||||
script_args.push(format!("bash <(curl -Ls https://raw.githubusercontent.com/ospab/ostp/master/scripts/install.sh) -v {}", v));
|
script_args.push(format!("bash <(curl -Ls https://raw.githubusercontent.com/ospab/ostp/master/scripts/install.sh) --branch {} -v {}", branch, v));
|
||||||
} else {
|
} else {
|
||||||
script_args.push("bash <(curl -Ls https://raw.githubusercontent.com/ospab/ostp/master/scripts/install.sh)".to_string());
|
script_args.push(format!("bash <(curl -Ls https://raw.githubusercontent.com/ospab/ostp/master/scripts/install.sh) --branch {}", branch));
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = Command::new("bash")
|
let status = Command::new("bash")
|
||||||
|
|
@ -2010,7 +2082,7 @@ fn cmd_update(version: Option<String>) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
fn cmd_update(_version: Option<String>) -> Result<()> {
|
fn cmd_update(branch: String, version: Option<String>) -> Result<()> {
|
||||||
anyhow::bail!("The 'update' command is only supported on Linux/Unix systems.");
|
anyhow::bail!("The 'update' command is only supported on Linux/Unix systems.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2085,7 +2157,7 @@ fn ensure_elevated_for_tun() -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if ret <= 32 {
|
if ret <= 32 {
|
||||||
anyhow::bail!("UAC elevation was denied or failed. Please run as Administrator.");
|
anyhow::bail!("UAC elevation was denied or failed (ret={}). Please run as Administrator.", ret);
|
||||||
}
|
}
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,17 @@ echo "Platform: linux/$ARCH"
|
||||||
|
|
||||||
# ── Parse arguments ────────────────────────────────────────────────────
|
# ── Parse arguments ────────────────────────────────────────────────────
|
||||||
TARGET_VERSION=""
|
TARGET_VERSION=""
|
||||||
|
TARGET_BRANCH="stable"
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-v|--version)
|
-v|--version)
|
||||||
TARGET_VERSION="$2"
|
TARGET_VERSION="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
-b|--branch)
|
||||||
|
TARGET_BRANCH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
@ -104,13 +109,21 @@ done
|
||||||
if [ -n "$TARGET_VERSION" ]; then
|
if [ -n "$TARGET_VERSION" ]; then
|
||||||
LATEST_RELEASE="$TARGET_VERSION"
|
LATEST_RELEASE="$TARGET_VERSION"
|
||||||
# Ensure it starts with 'v' if it's supposed to
|
# Ensure it starts with 'v' if it's supposed to
|
||||||
if [[ ! "$LATEST_RELEASE" =~ ^v ]]; then
|
if [[ ! "$LATEST_RELEASE" =~ ^v ]] && [ "$TARGET_BRANCH" == "stable" ]; then
|
||||||
LATEST_RELEASE="v$LATEST_RELEASE"
|
LATEST_RELEASE="v$LATEST_RELEASE"
|
||||||
fi
|
fi
|
||||||
echo "Fetching requested release $LATEST_RELEASE..."
|
echo "Fetching requested release $LATEST_RELEASE..."
|
||||||
else
|
else
|
||||||
echo "Fetching latest release..."
|
if [ "$TARGET_BRANCH" == "nightly" ]; then
|
||||||
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
echo "Fetching nightly release..."
|
||||||
|
LATEST_RELEASE="nightly"
|
||||||
|
elif [ "$TARGET_BRANCH" == "pre-release" ]; then
|
||||||
|
echo "Fetching pre-release..."
|
||||||
|
LATEST_RELEASE="pre-release"
|
||||||
|
else
|
||||||
|
echo "Fetching latest stable release..."
|
||||||
|
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$LATEST_RELEASE" ] || [[ "$LATEST_RELEASE" == *"null"* ]]; then
|
if [ -z "$LATEST_RELEASE" ] || [[ "$LATEST_RELEASE" == *"null"* ]]; then
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue