use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use rand::Rng;
use reqwest::blocking::Client;
use serde_json::Value;
use crate::config::{FirebaseConfig, FirebaseProjectInfo, TestResult};
pub fn generate_fid() -> String {
let mut rng = rand::thread_rng();
let mut bytes = [0u8; 17];
rng.fill(&mut bytes);
bytes[0] = (bytes[0] & 0x0F) | 0x70;
URL_SAFE_NO_PAD.encode(bytes)
}
pub fn test_firebase_installations(
client: &Client,
config: &FirebaseConfig,
info: &mut FirebaseProjectInfo,
) -> TestResult {
let (app_id, project_id) = match (&config.app_id, &config.project_id) {
(Some(a), Some(p)) => (a.clone(), p.clone()),
_ => {
return TestResult::fail(
None,
"Missing app_id or project_id",
Some("Firebase Installations requires app_id and project_id".into()),
)
}
};
let url = format!(
"https://firebaseinstallations.googleapis.com/v1/projects/{}/installations",
project_id
);
let fid = generate_fid();
let body = serde_json::json!({
"fid": fid,
"appId": app_id,
"authVersion": "FIS_v2",
"sdkVersion": "a:17.2.0",
});
let resp = match client
.post(&url)
.header("X-Goog-Api-Key", &config.api_key)
.header(
"x-firebase-client",
"fire-installations/17.2.0 android/34 fire-android/34.0.0",
)
.header("x-android-package", "com.flintbase.test")
.header("x-android-cert", "placeholder")
.json(&body)
.send()
{
Ok(r) => r,
Err(e) => return TestResult::fail(None, e.to_string(), None),
};
let status = resp.status().as_u16();
let data: Value = resp.json().unwrap_or_default();
match status {
200 => {
info.installations_api_enabled = Some(true);
let mut result = TestResult::ok(
status,
"Firebase Installations API accessible — device registration works",
);
if let Some(f) = data.get("fid").and_then(|v| v.as_str()) {
result = result.with_extra("fid", f);
}
if let Some(rt) = data.get("refreshToken").and_then(|v| v.as_str()) {
let preview = if rt.len() > 30 {
format!("{}...", &rt[..30])
} else {
rt.to_string()
};
result = result.with_extra("refresh_token", preview);
}
if let Some(at) = data
.get("authToken")
.and_then(|v| v.get("token"))
.and_then(|v| v.as_str())
{
let preview = if at.len() > 30 {
format!("{}...", &at[..30])
} else {
at.to_string()
};
result = result.with_extra("auth_token", preview);
}
result
}
401 => TestResult::fail(
Some(status),
"Unauthorized",
Some("API key not valid or package/cert mismatch".into()),
),
403 => {
let msg = data
.get("error")
.and_then(|e| e.get("message"))
.and_then(|m| m.as_str())
.unwrap_or("Forbidden");
TestResult::fail(
Some(status),
msg,
Some("Firebase Installations API restricted".into()),
)
}
_ => {
let msg = data
.get("error")
.and_then(|e| e.get("message"))
.and_then(|m| m.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("HTTP {}", status));
TestResult::fail(Some(status), msg, None)
}
}
}