feat(rust): Store API endpoint per account for better environment isolation

- Add endpoint field to accounts database table with default to production API
- Update Account model to include endpoint field
- Add --endpoint flag to account add command only
- Remove ENTE_ENDPOINT environment variable support
- Update account list to display endpoints in readable format
- Each account now maintains its own endpoint, preventing confusion between test and production environments

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Manav Rathi
2025-08-22 20:25:39 +05:30
parent 6ae7aa70d6
commit f84bd20bbf
5 changed files with 35 additions and 16 deletions

View File

@@ -25,6 +25,10 @@ pub enum AccountSubcommands {
#[arg(long, default_value = "photos")]
app: String,
/// API endpoint (defaults to https://api.ente.io)
#[arg(long, default_value = "https://api.ente.io")]
endpoint: String,
/// Export directory path
#[arg(long)]
export_dir: Option<String>,

View File

@@ -18,8 +18,9 @@ pub async fn handle_account_command(cmd: AccountCommand, storage: &Storage) -> R
email,
password,
app,
endpoint,
export_dir,
} => add_account(storage, email, password, app, export_dir).await,
} => add_account(storage, email, password, app, endpoint, export_dir).await,
AccountSubcommands::Update { email, dir, app } => {
update_account(storage, &email, &dir, &app).await
}
@@ -36,14 +37,24 @@ async fn list_accounts(storage: &Storage) -> Result<()> {
}
println!("\nConfigured accounts:\n");
println!("{:<30} {:<10} {:<40}", "Email", "App", "Export Directory");
println!("{}", "-".repeat(80));
println!("{:<30} {:<10} {:<30} {:<40}", "Email", "App", "Endpoint", "Export Directory");
println!("{}", "-".repeat(110));
for account in accounts {
// Shorten endpoint display for better readability
let endpoint_display = if account.endpoint == "https://api.ente.io" {
"api.ente.io (prod)".to_string()
} else if account.endpoint.starts_with("http://localhost") {
format!("localhost:{}", account.endpoint.split(':').last().unwrap_or(""))
} else {
account.endpoint.clone()
};
println!(
"{:<30} {:<10} {:<40}",
"{:<30} {:<10} {:<30} {:<40}",
account.email,
format!("{:?}", account.app).to_lowercase(),
endpoint_display,
account.export_dir.as_deref().unwrap_or("Not configured")
);
}
@@ -56,6 +67,7 @@ async fn add_account(
email_arg: Option<String>,
password_arg: Option<String>,
app_arg: String,
endpoint: String,
export_dir_arg: Option<String>,
) -> Result<()> {
println!("\n=== Add Ente Account ===\n");
@@ -132,12 +144,9 @@ async fn add_account(
std::fs::create_dir_all(&export_path).map_err(crate::models::error::Error::Io)?;
}
// Initialize API client (use ENTE_ENDPOINT env var if set, otherwise default to production)
let api_endpoint = std::env::var("ENTE_ENDPOINT").ok();
if let Some(ref endpoint) = api_endpoint {
log::debug!("Using custom API endpoint: {endpoint}");
}
let api_client = ApiClient::new(api_endpoint)?;
// Initialize API client with the specified endpoint
log::info!("Using API endpoint: {}", endpoint);
let api_client = ApiClient::new(Some(endpoint.clone()))?;
let auth_client = AuthClient::new(&api_client);
println!("\nAuthenticating with Ente servers...");
@@ -231,6 +240,7 @@ async fn add_account(
email: email.clone(),
user_id: auth_response.id,
app,
endpoint: endpoint.clone(),
export_dir: Some(export_dir.clone()),
};
@@ -249,6 +259,7 @@ async fn add_account(
println!("\n✅ Account added successfully!");
println!(" Email: {email}");
println!(" App: {app:?}");
println!(" Endpoint: {endpoint}");
println!(" Export directory: {export_dir}");
Ok(())

View File

@@ -10,6 +10,7 @@ pub struct Account {
pub email: String,
pub user_id: i64,
pub app: App,
pub endpoint: String,
pub export_dir: Option<String>,
}

View File

@@ -21,12 +21,13 @@ impl<'a> AccountStore<'a> {
let now = current_timestamp();
self.conn.execute(
"INSERT INTO accounts (email, user_id, app, export_dir, created_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
"INSERT INTO accounts (email, user_id, app, endpoint, export_dir, created_at, updated_at)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
params![
account.email,
account.user_id,
format!("{:?}", account.app).to_lowercase(),
account.endpoint,
account.export_dir,
now,
now
@@ -39,7 +40,7 @@ impl<'a> AccountStore<'a> {
/// Get an account by email and app
pub fn get(&self, email: &str, app: App) -> Result<Option<Account>> {
let mut stmt = self.conn.prepare(
"SELECT id, email, user_id, app, export_dir FROM accounts
"SELECT id, email, user_id, app, endpoint, export_dir FROM accounts
WHERE email = ?1 AND app = ?2",
)?;
@@ -55,7 +56,7 @@ impl<'a> AccountStore<'a> {
/// List all accounts
pub fn list(&self) -> Result<Vec<Account>> {
let mut stmt = self.conn.prepare(
"SELECT id, email, user_id, app, export_dir FROM accounts
"SELECT id, email, user_id, app, endpoint, export_dir FROM accounts
ORDER BY email, app",
)?;
@@ -143,7 +144,8 @@ fn row_to_account(row: &Row) -> rusqlite::Result<Account> {
email: row.get(1)?,
user_id: row.get(2)?,
app,
export_dir: row.get(4)?,
endpoint: row.get(4)?,
export_dir: row.get(5)?,
})
}

View File

@@ -10,10 +10,11 @@ pub fn create_tables(conn: &Connection) -> Result<()> {
email TEXT NOT NULL,
user_id INTEGER NOT NULL,
app TEXT NOT NULL,
endpoint TEXT NOT NULL DEFAULT 'https://api.ente.io',
export_dir TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(email, app)
UNIQUE(email, app, endpoint)
)",
[],
)?;