use crate::bootstrap::state::AppState;
use shared::Response;
use tracing::warn;
use ulid::Ulid;
pub async fn handle(state: AppState, provider_id: String) -> Response {
let provider = match crate::store::git_providers::get(&state.db, &provider_id).await {
Ok(Some(p)) => p,
Ok(None) => return Response::err("NotFound", "Provider não encontrado"),
Err(e) => return Response::err("DatabaseError", e.to_string()),
};
let kind =
shared::GitProviderKind::from_str(&provider.kind).unwrap_or(shared::GitProviderKind::Gitea);
let client_id = provider.oauth_client_id.clone().unwrap_or_default();
if client_id.is_empty() {
return Response::err("InvalidInput", "Provider sem Client ID (OAuth)");
}
let redirect_uri = match crate::api::http::public_routes::callback_redirect_uri(&state, kind) {
Some(u) => u,
None => {
return Response::err(
"ServerDomainMissing",
"Não foi possível determinar a URL pública do daemon (configure [api] domain)",
);
}
};
if let Some(enc) = &provider.access_token_enc
&& let Ok(token) = state.secrets.decrypt(enc)
&& let Err(e) = crate::features::git_providers::ensure_redirect_uri(
kind,
&provider.base_url,
&token,
&client_id,
&redirect_uri,
)
.await
{
warn!(error = %e, "oauth: falha ao pré-sincronizar redirect URI (continuando)");
}
let csrf = Ulid::new().to_string();
state
.oauth_states
.lock()
.unwrap()
.insert(csrf.clone(), provider_id);
let url = crate::features::git_providers::authorize_url(
kind,
&provider.base_url,
&pct(&client_id),
&pct(&redirect_uri),
&pct(&csrf),
);
Response::OAuthUrl(url)
}
fn pct(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for b in s.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
out.push(b as char)
}
_ => out.push_str(&format!("%{b:02X}")),
}
}
out
}