pub mod database;
pub mod site;
pub mod user;
use std::time::Duration;
use reqwest::{
blocking::{Client, RequestBuilder},
header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE},
};
use serde::{de::DeserializeOwned, Serialize};
use crate::error::{AppError, AppResult};
pub struct ForgeClient {
pub api_key: String,
pub base_url: String,
pub version: String,
pub client: Client,
}
impl ForgeClient {
pub fn new(api_key: &str) -> AppResult<Self> {
let mut headers = HeaderMap::new();
headers.insert(
AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", api_key)).map_err(|_| {
AppError::ForgeAPIError("Unable to construct Authorization string".into())
})?,
);
headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
Ok(Self {
api_key: api_key.to_string(),
base_url: "https://forge.laravel.com".into(),
version: "v1".into(),
client: Client::builder()
.default_headers(headers)
.build()
.map_err(|_| AppError::ForgeAPIError("Unable to contruct request client".into()))?,
})
}
pub fn post_request<T: DeserializeOwned, U: Serialize>(
&self,
server_id: &str,
endpoint: &str,
request_data: &U,
) -> AppResult<T> {
let url = format!(
"{}/api/{}/servers/{}/{}",
self.base_url, self.version, server_id, endpoint
);
let request_builder = self.client.post(&url).json(request_data);
match self.send_request(request_builder)? {
Some(data) => Ok(data),
None => Err(AppError::ForgeAPIError(
"Expected response data, but received none.".to_string(),
)),
}
}
fn delete_request(&self, server_id: &str, endpoint: &str, resource_id: &str) -> AppResult<()> {
let url = format!(
"{}/api/{}/servers/{}/{}/{}",
self.base_url, self.version, server_id, endpoint, resource_id
);
let request_builder = self.client.delete(&url);
let _ = self.send_request::<()>(request_builder);
Ok(())
}
fn list_request<T: DeserializeOwned>(&self, server_id: &str, endpoint: &str) -> AppResult<T> {
let url = format!(
"{}/api/{}/servers/{}/{}",
self.base_url, self.version, server_id, endpoint
);
let request_builder = self.client.get(&url);
match self.send_request(request_builder)? {
Some(data) => Ok(data),
None => Err(AppError::ForgeAPIError(
"Expected response data, but received none.".to_string(),
)),
}
}
fn get_request<T: DeserializeOwned>(
&self,
server_id: &str,
endpoint: &str,
resource_id: &str,
) -> AppResult<T> {
let url = format!(
"{}/api/{}/servers/{}/{}/{}",
self.base_url, self.version, server_id, endpoint, resource_id
);
let request_builder = self.client.get(&url);
match self.send_request(request_builder)? {
Some(data) => Ok(data),
None => Err(AppError::ForgeAPIError(
"Expected response data, but received none.".to_string(),
)),
}
}
fn put_request<T: DeserializeOwned, U: Serialize>(
&self,
server_id: &str,
endpoint: &str,
resource_id: &str,
request_data: &U,
) -> AppResult<T> {
let url = format!(
"{}/api/{}/servers/{}/{}/{}",
self.base_url, self.version, server_id, endpoint, resource_id
);
let request_builder = self.client.put(&url).json(request_data);
match self.send_request(request_builder)? {
Some(data) => Ok(data),
None => Err(AppError::ForgeAPIError(
"Expected response data, but received none.".to_string(),
)),
}
}
fn send_request<T: DeserializeOwned>(
&self,
request_builder: RequestBuilder,
) -> AppResult<Option<T>> {
let response = request_builder
.send()
.map_err(|e| AppError::ForgeAPIError(format!("Request failed: {}", e)))?;
if !response.status().is_success() {
let status = response.status();
let error_text = response
.text()
.unwrap_or_else(|_| "No error details available".into());
return Err(AppError::ForgeAPIError(format!(
"Request returned error {}: {}",
status, error_text
)));
}
let response_text = response
.text()
.map_err(|e| AppError::ForgeAPIError(format!("Failed to read response text: {}", e)))?;
if response_text.trim().is_empty() {
return Ok(None);
}
let parsed_response: T = serde_json::from_str(&response_text)
.map_err(|e| AppError::ForgeAPIError(format!("Failed to parse response: {}", e)))?;
Ok(Some(parsed_response))
}
}