initla code
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
|||||||
|
target/
|
||||||
|
.DS_Store
|
||||||
|
.env*
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
*.cer
|
||||||
|
*.p12
|
||||||
|
*.diagnostic
|
||||||
|
*.log
|
||||||
|
*.dump
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
postgres-test-data/
|
||||||
|
local-test-data/
|
||||||
|
diagnostics/
|
||||||
|
release-archives/
|
||||||
|
*.credential-test
|
||||||
|
*.credentials-test
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
[package]
|
||||||
|
name = "azure-database-tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
rust-version = "1.85"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
postgres-integration = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1.43", features = ["fs", "io-util", "macros", "net", "process", "rt", "rt-multi-thread", "sync", "time"] }
|
||||||
|
ratatui = "0.29"
|
||||||
|
crossterm = "0.28"
|
||||||
|
async-trait = "0.1"
|
||||||
|
futures-util = "0.3"
|
||||||
|
tokio-util = "0.7"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
uuid = { version = "1", features = ["serde", "v4"] }
|
||||||
|
thiserror = "2"
|
||||||
|
zeroize = "1"
|
||||||
|
secrecy = "0.10"
|
||||||
|
getrandom = "0.3"
|
||||||
|
sha2 = "0.10"
|
||||||
|
bytes = "1"
|
||||||
|
directories = "6"
|
||||||
|
rustls = "0.23"
|
||||||
|
rustls-native-certs = "0.8"
|
||||||
|
rustls-pemfile = "2"
|
||||||
|
tokio-postgres = { version = "0.7", features = ["with-serde_json-1"] }
|
||||||
|
tokio-postgres-rustls = "0.13"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
|
||||||
|
keyring = "3"
|
||||||
|
arboard = "3"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Azure Database TUI
|
||||||
|
|
||||||
|
This repository contains a Rust TUI for administering Azure Database for
|
||||||
|
PostgreSQL flexible servers. It discovers servers through the current Azure CLI
|
||||||
|
tenant and connects to PostgreSQL using mandatory TLS.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Install Azure CLI and authenticate to the intended tenant. Network access to
|
||||||
|
the server endpoint, including private endpoint, firewall, and DNS access, is
|
||||||
|
required. Azure CLI visibility does not imply PostgreSQL authorization.
|
||||||
|
|
||||||
|
## Build and test
|
||||||
|
|
||||||
|
Use the pinned Rust toolchain with `cargo build`, `cargo test`, and the local
|
||||||
|
integration helper documented in `docs/operations.md`.
|
||||||
|
|
||||||
|
Supported release targets are macOS ARM64 and Windows x86_64. Raw SQL is sent
|
||||||
|
to PostgreSQL as entered; use it only when you understand its authority and
|
||||||
|
effects.
|
||||||
|
|
||||||
|
See [security](docs/security.md), [operations](docs/operations.md), and
|
||||||
|
[release](docs/release.md).
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "1.85.0"
|
||||||
|
components = ["rustfmt", "clippy"]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub struct AzureDiscovery;
|
||||||
|
pub struct AzureCliAdapter;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod process; pub mod azure_cli; pub mod postgres;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub struct PostgresConnector;
|
||||||
|
pub struct SessionManager;
|
||||||
|
pub struct SqlSession;
|
||||||
|
pub struct GuidedSession;
|
||||||
|
pub struct ConnectionRequest;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct CommandSpec;
|
||||||
|
pub trait CommandRunner {}
|
||||||
|
pub struct TokioCommandRunner;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
use crate::error::AppError;
|
||||||
|
pub struct AppController;
|
||||||
|
impl AppController { pub async fn new() -> Result<Self, AppError> { Err(AppError(crate::error::SafeError { kind: crate::error::ErrorKind::Internal, message: "Application unavailable", sqlstate: None, severity: crate::error::ErrorSeverity::Error, retryable: false, operation_id: None })) } pub async fn run(&mut self) -> Result<(), AppError> { Ok(()) } }
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct AppCommand;
|
||||||
|
#[derive(Debug, Clone)] pub struct AppEvent;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod controller; pub mod events; pub mod state;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct AppState;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct CatalogSnapshot;
|
||||||
|
#[derive(Debug, Clone)] pub struct SchemaMeta;
|
||||||
|
#[derive(Debug, Clone)] pub struct RelationMeta;
|
||||||
|
#[derive(Debug, Clone)] pub struct ColumnMeta;
|
||||||
|
#[derive(Debug, Clone)] pub struct KeysetDefinition;
|
||||||
|
#[derive(Debug, Clone)] pub struct FunctionMeta;
|
||||||
|
#[derive(Debug, Clone)] pub struct RoleMeta;
|
||||||
|
#[derive(Debug, Clone)] pub struct PrivilegeObservation;
|
||||||
|
#[derive(Debug, Clone)] pub struct DefaultPrivilegeObservation;
|
||||||
|
#[derive(Debug, Clone)] pub struct ServerCapabilities;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
macro_rules! text_id { ($name:ident) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name(String); impl $name { pub fn new(value: impl Into<String>) -> Self { Self(value.into()) } pub fn as_str(&self) -> &str { &self.0 } } }; }
|
||||||
|
text_id!(TenantId); text_id!(SubscriptionId); text_id!(AzureResourceId);
|
||||||
|
text_id!(CatalogGeneration); text_id!(RoleOid); text_id!(RelationOid);
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ProfileId(Uuid);
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct OperationId(Uuid);
|
||||||
|
impl ProfileId { pub fn new() -> Self { Self(Uuid::new_v4()) } }
|
||||||
|
impl OperationId { pub fn new() -> Self { Self(Uuid::new_v4()) } }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct Tenant;
|
||||||
|
#[derive(Debug, Clone)] pub struct Subscription;
|
||||||
|
#[derive(Debug, Clone)] pub struct FlexibleServer;
|
||||||
|
#[derive(Debug, Clone)] pub struct DatabaseRef;
|
||||||
|
#[derive(Debug, Clone)] pub struct DiscoveryFailure;
|
||||||
|
#[derive(Debug, Clone)] pub struct DiscoveryReport;
|
||||||
|
#[derive(Debug, Clone)] pub struct DiscoveryEvent;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub mod catalog;
|
||||||
|
pub mod ids;
|
||||||
|
pub mod inventory;
|
||||||
|
pub mod permissions;
|
||||||
|
pub mod profile;
|
||||||
|
pub mod sql;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct DesiredPrivilegeProfile;
|
||||||
|
#[derive(Debug, Clone)] pub struct PrivilegeScope;
|
||||||
|
#[derive(Debug, Clone)] pub struct PrivilegeKind;
|
||||||
|
#[derive(Debug, Clone)] pub struct RoleMembershipIntent;
|
||||||
|
#[derive(Debug, Clone)] pub struct Grantability;
|
||||||
|
#[derive(Debug, Clone)] pub struct PlanOperation;
|
||||||
|
#[derive(Debug, Clone)] pub struct PermissionPlan;
|
||||||
|
#[derive(Debug, Clone)] pub struct PlanResult;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct ProfileFile;
|
||||||
|
#[derive(Debug, Clone)] pub struct ConnectionProfile;
|
||||||
|
#[derive(Debug, Clone)] pub struct SecretReference;
|
||||||
|
#[derive(Debug, Clone)] pub struct TlsPolicy;
|
||||||
|
#[derive(Debug, Clone)] pub struct ConnectionDraft;
|
||||||
|
#[derive(Debug, Clone)] pub struct ProfileValidationError;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#[derive(Debug, Clone)] pub struct StatementRange;
|
||||||
|
pub struct SqlSubmission;
|
||||||
|
#[derive(Debug, Clone)] pub struct SqlRunRequest;
|
||||||
|
#[derive(Debug, Clone)] pub struct SqlExecutionEvent;
|
||||||
|
#[derive(Debug, Clone)] pub struct ResultSetMeta;
|
||||||
|
pub struct DisplayCell;
|
||||||
|
#[derive(Debug, Clone)] pub struct CopyDirection;
|
||||||
|
#[derive(Debug, Clone)] pub struct TransactionState;
|
||||||
|
#[derive(Debug, Clone)] pub struct SqlLexer;
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use crate::domain::ids::OperationId;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ErrorKind { Internal, Input, Network, Database, Process }
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ErrorSeverity { Info, Warning, Error }
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct SqlStateCode([u8; 5]);
|
||||||
|
|
||||||
|
impl SqlStateCode {
|
||||||
|
pub fn parse(value: &str) -> Option<Self> {
|
||||||
|
let bytes = value.as_bytes();
|
||||||
|
if bytes.len() == 5 && bytes.iter().all(|b| b.is_ascii_uppercase() || b.is_ascii_digit()) {
|
||||||
|
Some(Self(bytes.try_into().ok()?))
|
||||||
|
} else { None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SafeError {
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
pub message: &'static str,
|
||||||
|
pub sqlstate: Option<SqlStateCode>,
|
||||||
|
pub severity: ErrorSeverity,
|
||||||
|
pub retryable: bool,
|
||||||
|
pub operation_id: Option<OperationId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AppError(pub SafeError);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SafeDiagnostic {
|
||||||
|
pub error: SafeError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AppError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.message.fmt(f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for AppError {}
|
||||||
|
|
||||||
|
pub fn redact_sensitive_text(input: &str) -> String {
|
||||||
|
if input.is_empty() { String::new() } else { "[diagnostic details omitted]".to_owned() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod adapters;
|
||||||
|
pub mod app;
|
||||||
|
pub mod domain;
|
||||||
|
pub mod platform;
|
||||||
|
pub mod services;
|
||||||
|
pub mod tui;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
use azure_database_tui::app::controller::AppController;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), azure_database_tui::error::AppError> {
|
||||||
|
let mut controller = AppController::new().await?;
|
||||||
|
controller.run().await
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub trait ClipboardService {} pub struct SystemClipboardService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub trait Clock {} pub struct SystemClock;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub trait SecretStore {} pub struct KeyringSecretStore; pub struct UnavailableSecretStore;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod paths; pub mod clock; pub mod profile_file; pub mod keychain; pub mod clipboard; pub mod secret_input;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub struct AppPaths;
|
||||||
|
impl AppPaths { pub fn discover() -> Self { Self } }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct ProfileFileStore;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct SecretInputBuffer; pub struct SecretHandle; pub struct OneTimeSecret; pub struct SecretVault;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct CatalogService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct DiscoveryService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod discovery; pub mod profiles; pub mod catalog; pub mod tables; pub mod roles; pub mod sql_executor;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct ProfileService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct RoleService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct SqlExecutor;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct TableService;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub struct TerminalGuard;
|
||||||
Reference in New Issue
Block a user