Skip to content

Commit df1fb9d

Browse files
committed
update docker deps and CLI
1 parent e23420c commit df1fb9d

File tree

3 files changed

+163
-50
lines changed

3 files changed

+163
-50
lines changed

build-docker/alpine/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ RUN apk add --no-cache \
1010
perl \
1111
perl-utils \
1212
perl-dev \
13+
sqlite-dev \
1314
openssl-dev
1415

1516
COPY . .
17+
RUN cargo build -p router-cli --release
1618
RUN cargo build -p router-core --release
1719
RUN cargo build -p router-api --release
18-
RUN cargo build -p router-cli --release
1920

2021
# Runtime image stage
2122
FROM alpine:latest
@@ -24,7 +25,8 @@ WORKDIR /app
2425
# Install minimal runtime dependencies
2526
RUN apk add --no-cache \
2627
ca-certificates \
27-
bash
28+
bash \
29+
sqlite-libs
2830

2931
# Copy binaries from builder
3032
COPY --from=builder /app/target/release/router-core /usr/local/bin/router-core

router-cli/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
mini-config = { workspace = true }
88
crossterm = { workspace = true }
99
clap = { version = "4.5.35", features = ["derive"] }
10-
reqwest = { version = "0.12.4", features = ["json", "blocking"] }
10+
ureq = { version = "2.9.6", features = ["json"] }
1111
serde = { version = "1.0.196", features = ["derive"] }
1212
tokio = { version = "1.35.1", features = ["full"] }
1313
serde_json = "1.0.113"
@@ -16,5 +16,4 @@ anyhow = "1.0.79"
1616
thiserror = "1.0.56"
1717
log = "0.4.20"
1818
env_logger = "0.11.1"
19-
dirs = "5.0.1"
20-
openssl = "0.10.72"
19+
dirs = "5.0.1"

router-cli/src/main.rs

Lines changed: 157 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
use anyhow::{Context, Result};
2-
use clap::Parser;
2+
use clap::{Parser, Subcommand};
33
use log::{debug, error, info};
4-
use reqwest::{blocking::Client, header};
54
use serde::{Deserialize, Serialize};
6-
use std::{env, fs::File, io::Read, path::PathBuf};
5+
use std::{env, fs::File, io::{Read, Write}, path::PathBuf};
76

87
/// Mini-Gateway Router CLI Tool
98
#[derive(Parser)]
109
#[command(name = "gwrs")]
1110
#[command(about = "CLI tool for Mini-Gateway Router API", long_about = None)]
1211
struct Cli {
1312
/// Path to the configuration file
14-
#[arg(long, required = true)]
15-
config: PathBuf,
13+
#[arg(long)]
14+
config: Option<PathBuf>,
1615

1716
/// Use credentials from OS environment variables (GWRS_USER, GWRS_PASS)
1817
#[arg(long, global = true)]
@@ -29,6 +28,24 @@ struct Cli {
2928
/// API base URL (default: http://localhost:24042)
3029
#[arg(long, global = true, default_value = "http://localhost:24042")]
3130
url: String,
31+
32+
#[command(subcommand)]
33+
command: Option<Commands>,
34+
}
35+
36+
#[derive(Subcommand)]
37+
enum Commands {
38+
/// Initialize a new configuration file
39+
Init {
40+
/// Location to create the configuration file (default: current directory)
41+
#[arg(value_name = "LOCATION")]
42+
location: Option<PathBuf>,
43+
},
44+
/// Upload configuration to the router
45+
Config {
46+
/// Path to the configuration file
47+
config: PathBuf,
48+
},
3249
}
3350

3451
#[derive(Serialize, Deserialize, Debug)]
@@ -66,26 +83,64 @@ fn main() -> Result<()> {
6683
env_logger::init();
6784
let cli = Cli::parse();
6885

69-
// Get credentials
70-
let (username, password) = get_credentials(&cli)?;
71-
72-
debug!("Using API URL: {}", cli.url);
73-
debug!("Using username: {}", username);
74-
75-
// Create HTTP client
76-
let client = Client::new();
77-
78-
// Authenticate and get token
79-
let token = authenticate(&client, &cli.url, &username, &password)?;
80-
debug!("Authentication successful, token received");
81-
82-
// Upload config
83-
upload_config(&client, &cli.url, &token, &cli.config)?;
86+
match cli.command {
87+
Some(Commands::Init { location }) => {
88+
init_config(&location.unwrap_or_else(|| PathBuf::from(".")))?;
89+
}
90+
Some(Commands::Config { config }) => {
91+
// Get credentials
92+
let (username, password) = get_credentials(&Credentials {
93+
osenv: cli.osenv,
94+
user: cli.user,
95+
pass: cli.pass
96+
})?;
97+
98+
debug!("Using API URL: {}", cli.url);
99+
debug!("Using username: {}", username);
100+
101+
// Authenticate and get token
102+
let token = authenticate(&cli.url, &username, &password)?;
103+
debug!("Authentication successful, token received");
104+
105+
// Upload config
106+
upload_config(&cli.url, &token, &config)?;
107+
}
108+
None => {
109+
if let Some(config) = cli.config {
110+
// Get credentials
111+
let (username, password) = get_credentials(&Credentials {
112+
osenv: cli.osenv,
113+
user: cli.user,
114+
pass: cli.pass
115+
})?;
116+
117+
debug!("Using API URL: {}", cli.url);
118+
debug!("Using username: {}", username);
119+
120+
// Authenticate and get token
121+
let token = authenticate(&cli.url, &username, &password)?;
122+
debug!("Authentication successful, token received");
123+
124+
// Upload config
125+
upload_config(&cli.url, &token, &config)?;
126+
} else {
127+
error!("No configuration file specified. Use --config or the config subcommand");
128+
anyhow::bail!("No configuration file specified. Use --config or the config subcommand");
129+
}
130+
}
131+
}
84132

85133
Ok(())
86134
}
87135

88-
fn get_credentials(cli: &Cli) -> Result<(String, String)> {
136+
#[derive(Debug)]
137+
struct Credentials {
138+
osenv: bool,
139+
user: Option<String>,
140+
pass: Option<String>,
141+
}
142+
143+
fn get_credentials(cli: &Credentials) -> Result<(String, String)> {
89144
if cli.osenv {
90145
debug!("Getting credentials from environment variables");
91146
let username = env::var("GWRS_USER").context("GWRS_USER environment variable not set")?;
@@ -100,7 +155,77 @@ fn get_credentials(cli: &Cli) -> Result<(String, String)> {
100155
}
101156
}
102157

103-
fn authenticate(client: &Client, base_url: &str, username: &str, password: &str) -> Result<String> {
158+
fn init_config(location: &PathBuf) -> Result<()> {
159+
info!("Initializing configuration file in: {}", location.display());
160+
161+
let config_path = location.join("router-config.yaml");
162+
let config_content = r#"# Mini-Gateway Router Configuration
163+
# This file contains the configuration for your router setup
164+
165+
proxy:
166+
- name: "proxy1"
167+
listen: "127.0.0.1:8080"
168+
domains:
169+
- domain: "example.com"
170+
tls: false
171+
tls_cert: |
172+
-----BEGIN CERTIFICATE-----
173+
cert
174+
-----END CERTIFICATE-----
175+
tls_key: |
176+
-----BEGIN PRIVATE KEY-----
177+
key
178+
-----END PRIVATE KEY-----
179+
highspeed:
180+
enabled: true
181+
target: "gateway1"
182+
gateway:
183+
- name: "gateway1"
184+
domain: "example.com"
185+
target: "127.0.0.1:8080"
186+
path:
187+
- priority: 1
188+
pattern: "^(.*)$"
189+
target: "/$1"
190+
"#;
191+
192+
let mut file = File::create(&config_path)
193+
.context("Failed to create configuration file")?;
194+
195+
file.write_all(config_content.as_bytes())
196+
.context("Failed to write configuration file")?;
197+
198+
info!("Configuration file created at: {}", config_path.display());
199+
println!("Configuration file created at: {}", config_path.display());
200+
println!("\nConfiguration file structure explanation:");
201+
println!("1. proxy: Define your proxy servers with their settings");
202+
println!(" - name: Unique identifier for the proxy");
203+
println!(" - listen: Address and port to listen on");
204+
println!(" - domains: List of domains this proxy handles");
205+
println!(" - domain: Domain name");
206+
println!(" - tls: Enable/disable TLS");
207+
println!(" - tls_cert: TLS certificate (if tls is true)");
208+
println!(" - tls_key: TLS private key (if tls is true)");
209+
println!(" - highspeed: High-speed routing settings");
210+
println!(" - enabled: Enable/disable high-speed routing");
211+
println!(" - target: Target gateway for high-speed routing");
212+
println!(" - gateway: List of gateways for this proxy");
213+
println!(" - name: Gateway name");
214+
println!(" - domain: Domain for this gateway");
215+
println!(" - target: Target address and port");
216+
println!(" - path: URL path routing rules");
217+
println!(" - priority: Rule priority (lower numbers = higher priority)");
218+
println!(" - pattern: Regex pattern to match");
219+
println!(" - target: Target path pattern");
220+
println!("\nTo use this configuration:");
221+
println!("1. Edit the file to match your setup");
222+
println!("2. Use 'gwrs config router-config.yaml' to upload it");
223+
println!("3. Add authentication with --user/--pass or --osenv");
224+
225+
Ok(())
226+
}
227+
228+
fn authenticate(base_url: &str, username: &str, password: &str) -> Result<String> {
104229
info!("Authenticating with username: {}", username);
105230

106231
let login_url = format!("{}/api/v1/users/login", base_url);
@@ -109,14 +234,12 @@ fn authenticate(client: &Client, base_url: &str, username: &str, password: &str)
109234
password: password.to_string(),
110235
};
111236

112-
let response = client
113-
.post(&login_url)
114-
.json(&login_request)
115-
.send()
237+
let response = ureq::post(&login_url)
238+
.send_json(ureq::json!(login_request))
116239
.context("Failed to send login request")?;
117240

118241
let login_response = response
119-
.json::<LoginResponse>()
242+
.into_json::<LoginResponse>()
120243
.context("Failed to parse login response")?;
121244

122245
if !login_response.success {
@@ -134,7 +257,6 @@ fn authenticate(client: &Client, base_url: &str, username: &str, password: &str)
134257
}
135258

136259
fn upload_config(
137-
client: &Client,
138260
base_url: &str,
139261
token: &str,
140262
config_path: &PathBuf,
@@ -155,36 +277,26 @@ fn upload_config(
155277

156278
// Prepare request
157279
let upload_url = format!("{}/api/v1/settings/auto-config", base_url);
158-
let mut headers = header::HeaderMap::new();
159-
headers.insert(
160-
header::AUTHORIZATION,
161-
header::HeaderValue::from_str(&format!("Bearer {}", token))?,
162-
);
163-
headers.insert(
164-
header::CONTENT_TYPE,
165-
header::HeaderValue::from_static("application/yaml"),
166-
);
167280

168281
// Send request
169-
let response = client
170-
.post(&upload_url)
171-
.headers(headers)
172-
.body(contents)
173-
.send()
282+
let response = ureq::post(&upload_url)
283+
.set("Authorization", &format!("Bearer {}", token))
284+
.set("Content-Type", "application/yaml")
285+
.send_string(&contents)
174286
.context("Failed to send configuration upload request")?;
175287

176288
// Check status
177289
let status = response.status();
178-
if !status.is_success() {
290+
if status >= 400 {
179291
let error_text = response
180-
.text()
292+
.into_string()
181293
.unwrap_or_else(|_| "Unknown error".to_string());
182294
error!("Upload failed with status {}: {}", status, error_text);
183295
anyhow::bail!("Upload failed with status {}: {}", status, error_text);
184296
}
185297

186298
let upload_response = response
187-
.json::<ConfigUploadResponse>()
299+
.into_json::<ConfigUploadResponse>()
188300
.context("Failed to parse upload response")?;
189301

190302
if let Some(error) = upload_response.error {

0 commit comments

Comments
 (0)