Skip to content

Commit 3edcad0

Browse files
feat: Refactor rust code
1 parent 6fb86a7 commit 3edcad0

9 files changed

Lines changed: 698 additions & 641 deletions

File tree

src-tauri/src/alerts.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use tauri::Manager;
2+
use crate::models::{AlertsResponse, RepoAlerts, GitHubAlert};
3+
use crate::state::AppState;
4+
5+
// ============================================================================
6+
// Security Alerts Commands
7+
// ============================================================================
8+
9+
#[tauri::command]
10+
pub async fn get_github_security_alerts(app: tauri::AppHandle) -> Result<AlertsResponse, String> {
11+
let (token, repos) = {
12+
let state = app.try_state::<AppState>().ok_or("No state")?;
13+
let config = state.config.lock().unwrap();
14+
(
15+
config.access_token.clone().ok_or("Not authenticated")?,
16+
config.selected_repos.clone(),
17+
)
18+
};
19+
20+
if repos.is_empty() {
21+
return Ok(AlertsResponse {
22+
total_alerts: 0,
23+
repos: vec![],
24+
});
25+
}
26+
27+
let mut total_alerts = 0;
28+
let mut repo_alerts = Vec::new();
29+
let client = reqwest::Client::new();
30+
31+
for repo in repos {
32+
let url = format!(
33+
"https://api.github.com/repos/{}/dependabot/alerts",
34+
repo
35+
);
36+
37+
match client
38+
.get(&url)
39+
.header("Accept", "application/vnd.github+json")
40+
.header("Authorization", format!("Bearer {}", token))
41+
.header("User-Agent", "github-security-alerts")
42+
.send()
43+
.await
44+
{
45+
Ok(response) => {
46+
match response.json::<Vec<GitHubAlert>>().await {
47+
Ok(alerts) => {
48+
let open_alerts = alerts.iter()
49+
.filter(|a| a.state == "open")
50+
.count();
51+
total_alerts += open_alerts;
52+
repo_alerts.push(RepoAlerts {
53+
name: repo,
54+
alerts: open_alerts,
55+
});
56+
}
57+
Err(e) => {
58+
eprintln!("Failed to parse alerts for {}: {}", repo, e);
59+
repo_alerts.push(RepoAlerts {
60+
name: repo,
61+
alerts: 0,
62+
});
63+
}
64+
}
65+
}
66+
Err(e) => {
67+
eprintln!("Failed to fetch alerts for {}: {}", repo, e);
68+
repo_alerts.push(RepoAlerts {
69+
name: repo,
70+
alerts: 0,
71+
});
72+
}
73+
}
74+
}
75+
76+
Ok(AlertsResponse {
77+
total_alerts,
78+
repos: repo_alerts,
79+
})
80+
}

src-tauri/src/auth.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use tauri::Manager;
2+
use crate::models::{AuthStatus, GitHubUser};
3+
use crate::state::AppState;
4+
use crate::config::save_config;
5+
6+
// ============================================================================
7+
// Authentication Commands
8+
// ============================================================================
9+
10+
/// Set a Personal Access Token directly
11+
#[tauri::command]
12+
pub async fn set_token(app: tauri::AppHandle, token: String) -> Result<(), String> {
13+
// Verify token by fetching user info
14+
let client = reqwest::Client::new();
15+
let response = client
16+
.get("https://api.github.com/user")
17+
.header("Authorization", format!("Bearer {}", token))
18+
.header("User-Agent", "github-security-alerts")
19+
.send()
20+
.await
21+
.map_err(|e| format!("Failed to verify token: {}", e))?;
22+
23+
if !response.status().is_success() {
24+
return Err("Invalid token".to_string());
25+
}
26+
27+
// Token is valid, save it
28+
if let Some(state) = app.try_state::<AppState>() {
29+
let mut config = state.config.lock().unwrap();
30+
config.access_token = Some(token);
31+
save_config(&config)?;
32+
}
33+
34+
Ok(())
35+
}
36+
37+
#[tauri::command]
38+
pub async fn get_auth_status(app: tauri::AppHandle) -> Result<AuthStatus, String> {
39+
let token = {
40+
let state = app.try_state::<AppState>().ok_or("No state")?;
41+
let config = state.config.lock().unwrap();
42+
config.access_token.clone()
43+
};
44+
45+
match token {
46+
Some(token) => {
47+
// Verify token by fetching user info
48+
let client = reqwest::Client::new();
49+
let response = client
50+
.get("https://api.github.com/user")
51+
.header("Authorization", format!("Bearer {}", token))
52+
.header("User-Agent", "github-security-alerts")
53+
.send()
54+
.await;
55+
56+
match response {
57+
Ok(resp) if resp.status().is_success() => {
58+
let user: GitHubUser = resp.json().await.map_err(|e| e.to_string())?;
59+
Ok(AuthStatus {
60+
authenticated: true,
61+
username: Some(user.login),
62+
})
63+
}
64+
_ => {
65+
// Token is invalid, clear it
66+
if let Some(state) = app.try_state::<AppState>() {
67+
let mut config = state.config.lock().unwrap();
68+
config.access_token = None;
69+
let _ = save_config(&config);
70+
}
71+
Ok(AuthStatus {
72+
authenticated: false,
73+
username: None,
74+
})
75+
}
76+
}
77+
}
78+
None => Ok(AuthStatus {
79+
authenticated: false,
80+
username: None,
81+
}),
82+
}
83+
}
84+
85+
#[tauri::command]
86+
pub async fn logout(app: tauri::AppHandle) -> Result<(), String> {
87+
if let Some(state) = app.try_state::<AppState>() {
88+
let mut config = state.config.lock().unwrap();
89+
config.access_token = None;
90+
config.selected_repos = vec![];
91+
save_config(&config)?;
92+
}
93+
Ok(())
94+
}

src-tauri/src/config.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::fs;
2+
use std::path::PathBuf;
3+
use crate::models::AppConfig;
4+
5+
// ============================================================================
6+
// Config File Management
7+
// ============================================================================
8+
9+
pub fn get_config_path() -> PathBuf {
10+
let config_dir = dirs::config_dir()
11+
.unwrap_or_else(|| PathBuf::from("."))
12+
.join("github-security-alerts");
13+
14+
fs::create_dir_all(&config_dir).ok();
15+
config_dir.join("config.json")
16+
}
17+
18+
pub fn load_config() -> AppConfig {
19+
let path = get_config_path();
20+
if path.exists() {
21+
if let Ok(content) = fs::read_to_string(&path) {
22+
if let Ok(config) = serde_json::from_str(&content) {
23+
return config;
24+
}
25+
}
26+
}
27+
AppConfig::default()
28+
}
29+
30+
pub fn save_config(config: &AppConfig) -> Result<(), String> {
31+
let path = get_config_path();
32+
let content = serde_json::to_string_pretty(config)
33+
.map_err(|e| e.to_string())?;
34+
fs::write(path, content).map_err(|e| e.to_string())?;
35+
Ok(())
36+
}

0 commit comments

Comments
 (0)