mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add web adapter for vehicle factory
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2032,9 +2032,7 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
|
||||
[[package]]
|
||||
name = "libfj"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f55afe6b910429afcd02d7810760e07e8489ad662f7df0e938a6154fe413d41"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
|
||||
@@ -30,8 +30,8 @@ members = [
|
||||
[workspace.dependencies]
|
||||
actix-web = { version = "4", default-features = false, features = [ "macros", "compress-brotli", "compress-gzip", "compress-zstd", "ws"] }
|
||||
actix-files = "0.6"
|
||||
#libfj = { version = "0.9", path = "../libfj" }
|
||||
libfj = { version = "0.9" }
|
||||
libfj = { version = "0.10", path = "../libfj" }
|
||||
#libfj = { version = "0.9" }
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
clap = { version = "4.5", features = [ "derive" ] }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub enum Factory {
|
||||
Arc(oj_rc_factory::arc::ArcAdapter),
|
||||
Primary(oj_rc_database::FactoryDatabase),
|
||||
Web(oj_rc_factory::web::WebAdapter),
|
||||
Custom(Box<dyn oj_rc_factory::VehicleFactoryAdapter + Send + Sync + 'static>),
|
||||
None,
|
||||
}
|
||||
@@ -11,6 +12,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.vehicle(id).await,
|
||||
Self::Primary(x) => x.vehicle(id).await,
|
||||
Self::Web(x) => x.vehicle(id).await,
|
||||
Self::Custom(x) => x.vehicle(id).await,
|
||||
Self::None => Ok(None),
|
||||
}
|
||||
@@ -20,6 +22,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.list(query).await,
|
||||
Self::Primary(x) => x.list(query).await,
|
||||
Self::Web(x) => x.list(query).await,
|
||||
Self::Custom(x) => x.list(query).await,
|
||||
Self::None => Ok(Vec::default()),
|
||||
}
|
||||
@@ -29,6 +32,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.upload(vehicle).await,
|
||||
Self::Primary(x) => x.upload(vehicle).await,
|
||||
Self::Web(x) => x.upload(vehicle).await,
|
||||
Self::Custom(x) => x.upload(vehicle).await,
|
||||
Self::None => Ok(oj_rc_factory::VehicleThumbnailInfo {
|
||||
id: i32::MIN,
|
||||
@@ -42,6 +46,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||
Self::Primary(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||
Self::Web(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||
Self::Custom(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||
Self::None => Ok(()),
|
||||
}
|
||||
@@ -51,6 +56,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.purchase(id).await,
|
||||
Self::Primary(x) => x.purchase(id).await,
|
||||
Self::Web(x) => x.purchase(id).await,
|
||||
Self::Custom(x) => x.purchase(id).await,
|
||||
Self::None => Ok(()),
|
||||
}
|
||||
@@ -60,6 +66,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
||||
Self::Primary(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
||||
Self::Web(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
||||
Self::Custom(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
||||
Self::None => Ok(()),
|
||||
}
|
||||
@@ -69,6 +76,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.remove_vehicle(id, user_id).await,
|
||||
Self::Primary(x) => x.remove_vehicle(id, user_id).await,
|
||||
Self::Web(x) => x.remove_vehicle(id, user_id).await,
|
||||
Self::Custom(x) => x.remove_vehicle(id, user_id).await,
|
||||
Self::None => Ok(()),
|
||||
}
|
||||
@@ -78,6 +86,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
||||
match self {
|
||||
Self::Arc(x) => x.set_featured(id, is_featured).await,
|
||||
Self::Primary(x) => x.set_featured(id, is_featured).await,
|
||||
Self::Web(x) => x.set_featured(id, is_featured).await,
|
||||
Self::Custom(x) => x.set_featured(id, is_featured).await,
|
||||
Self::None => Ok(()),
|
||||
}
|
||||
@@ -89,6 +98,7 @@ impl Factory {
|
||||
Ok(match &conf.adapter {
|
||||
crate::persist::AdapterSettings::Arc(x) => Self::Arc(oj_rc_factory::arc::ArcAdapter::init(&x.uri, x.show_expired, settings.cdn_url.to_owned(), x.override_cdn, x.spoof_username).await?),
|
||||
crate::persist::AdapterSettings::BuiltIn => Self::Primary(builtin_factory_provider()),
|
||||
crate::persist::AdapterSettings::Web(x) => Self::Web(oj_rc_factory::web::WebAdapter::init(&x.url, &settings.auth_url).await?),
|
||||
crate::persist::AdapterSettings::None => Self::None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ pub enum AdapterSettings {
|
||||
Arc(ArcFactorySettings),
|
||||
#[serde(alias = "integrated")]
|
||||
BuiltIn,
|
||||
#[serde(alias = "online")]
|
||||
Web(WebFactorySettings),
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -42,3 +44,8 @@ pub struct ArcFactorySettings {
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct WebFactorySettings {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod arc;
|
||||
pub mod web;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{VehicleFactoryAdapter, VehicleInfo, VehicleQueryInfo, VehicleUploadInfo, VehicleThumbnailInfo};
|
||||
|
||||
151
rc_factory/src/web/adapter.rs
Normal file
151
rc_factory/src/web/adapter.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
pub struct WebAdapter {
|
||||
api: libfj::robocraft::FactoryAPI,
|
||||
}
|
||||
|
||||
// TODO do some real authentication
|
||||
struct FederatedFactoryAuthProvider {
|
||||
target_domain: String,
|
||||
my_auth_url: String,
|
||||
}
|
||||
|
||||
impl libfj::robocraft::ITokenProvider for FederatedFactoryAuthProvider {
|
||||
fn token(&self) -> Result<String, ()> {
|
||||
Ok(format!("{},{}", self.target_domain, self.my_auth_url))
|
||||
}
|
||||
|
||||
fn scheme(&self) -> &'static str {
|
||||
"OJWeb"
|
||||
}
|
||||
}
|
||||
|
||||
impl WebAdapter {
|
||||
pub async fn init(url: &str, auth_url: &str) -> Result<Self, Box<dyn std::error::Error + 'static>> {
|
||||
Ok(Self {
|
||||
api: libfj::robocraft::FactoryAPI::with_auth(FederatedFactoryAuthProvider {
|
||||
target_domain: url.to_owned(),
|
||||
my_auth_url: auth_url.to_owned(),
|
||||
})
|
||||
.with_domain(url)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl crate::VehicleFactoryAdapter for WebAdapter {
|
||||
async fn vehicle(&self, id: i32) -> Result<Option<(crate::VehicleInfo, crate::VehicleQueryInfo)>, Box<dyn std::error::Error>> {
|
||||
use base64::Engine;
|
||||
let id_usize: usize = id.try_into()?;
|
||||
let response = self.api.get(id_usize).await?;
|
||||
let response_data = response.response;
|
||||
Ok(Some((
|
||||
crate::VehicleInfo {
|
||||
id,
|
||||
cube_data: base64::prelude::BASE64_STANDARD.decode(response_data.cube_data.as_bytes()).unwrap_or_default(),
|
||||
colour_data: base64::prelude::BASE64_STANDARD.decode(response_data.colour_data.as_bytes()).unwrap_or_default(),
|
||||
},
|
||||
crate::VehicleQueryInfo {
|
||||
id: response_data.item_id as _,
|
||||
name: response_data.item_name,
|
||||
description: response_data.item_description,
|
||||
thumbnail: response_data.thumbnail,
|
||||
added_by: response_data.added_by,
|
||||
added_by_display_name: response_data.added_by_display_name,
|
||||
added_date: crate::traits::parse_rc_date(&response_data.added_date).unwrap_or_default(),
|
||||
expiry_date: crate::traits::parse_rc_date(&response_data.expiry_date).unwrap_or_default(),
|
||||
cpu: response_data.cpu as _,
|
||||
total_robot_ranking: response_data.total_robot_ranking as _,
|
||||
rent_count: response_data.rent_count as _,
|
||||
buy_count: response_data.buy_count as _,
|
||||
buyable: response_data.buyable,
|
||||
removed_date: response_data.removed_date.and_then(|x| crate::traits::parse_rc_date(&x).ok()),
|
||||
ban_date: response_data.ban_date.and_then(|x| crate::traits::parse_rc_date(&x).ok()),
|
||||
featured: response_data.featured,
|
||||
banner_message: response_data.banner_message,
|
||||
combat_rating: response_data.combat_rating as _,
|
||||
cosmetic_rating: response_data.cosmetic_rating as _,
|
||||
cube_amounts: serde_json::from_str(&response_data.cube_amounts).unwrap_or_default(),
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
async fn list(&self, query: libfj::robocraft::ListQuery) -> Result<Vec<crate::VehicleQueryInfo>, Box<dyn std::error::Error>> {
|
||||
let response = if query.default_page {
|
||||
self.api.list().await?
|
||||
} else {
|
||||
self.api.list_builder()
|
||||
.page(query.page as _)
|
||||
.items_per_page(query.page_size as _)
|
||||
.order(query.order)
|
||||
.movement_raw(concat_u32_enums_to_str(&query.movement_filter))
|
||||
.weapon_raw(concat_u32_enums_to_str(&query.weapon_filter))
|
||||
.min_cpu(query.minimum_cpu as _)
|
||||
.max_cpu(query.maximum_cpu as _)
|
||||
.text(query.text_filter)
|
||||
.text_search_type(if query.player_filter { libfj::robocraft::FactoryTextSearchField::Player } else { query.text_search_field })
|
||||
.buyable(query.buyable)
|
||||
.prepend_featured(query.prepend_featured_robot)
|
||||
.default_page(query.default_page)
|
||||
.send().await?
|
||||
};
|
||||
Ok(response.response.roboshop_items.into_iter().map(|response_data| crate::VehicleQueryInfo {
|
||||
id: response_data.item_id as _,
|
||||
name: response_data.item_name,
|
||||
description: response_data.item_description,
|
||||
thumbnail: response_data.thumbnail,
|
||||
added_by: response_data.added_by,
|
||||
added_by_display_name: response_data.added_by_display_name,
|
||||
added_date: crate::traits::parse_rc_date(&response_data.added_date).unwrap_or_default(),
|
||||
expiry_date: crate::traits::parse_rc_date(&response_data.expiry_date).unwrap_or_default(),
|
||||
cpu: response_data.cpu as _,
|
||||
total_robot_ranking: response_data.total_robot_ranking as _,
|
||||
rent_count: response_data.rent_count as _,
|
||||
buy_count: response_data.buy_count as _,
|
||||
buyable: response_data.buyable,
|
||||
removed_date: response_data.removed_date.and_then(|x| crate::traits::parse_rc_date(&x).ok()),
|
||||
ban_date: response_data.ban_date.and_then(|x| crate::traits::parse_rc_date(&x).ok()),
|
||||
featured: response_data.featured,
|
||||
banner_message: response_data.banner_message,
|
||||
combat_rating: response_data.combat_rating as _,
|
||||
cosmetic_rating: response_data.cosmetic_rating as _,
|
||||
cube_amounts: serde_json::from_str(&response_data.cube_amounts).unwrap_or_default(),
|
||||
}).collect())
|
||||
}
|
||||
|
||||
async fn upload(&self, _vehicle: crate::VehicleUploadInfo) -> Result<crate::VehicleThumbnailInfo, Box<dyn std::error::Error>>{
|
||||
Err("Uploading is not supported in the web adapter".into())
|
||||
}
|
||||
|
||||
async fn rate_vehicle(&self, _id: i32, _combat: i32, _cosmetic: i32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("Rating is not supported in the web adapter".into())
|
||||
}
|
||||
|
||||
/// Just update any purchase trackers
|
||||
async fn purchase(&self, _id: i32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_vehicle(&self, _id: i32, _cube_data: Option<Vec<u8>>, _colour_data: Option<Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("Updating vehicles is not supported in the web adapter".into())
|
||||
}
|
||||
|
||||
async fn remove_vehicle(&self, _id: i32, _user_id: i32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("Removing vehicles is not supported in the web adapter".into())
|
||||
}
|
||||
|
||||
async fn set_featured(&self, _id: i32, _is_featured: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("Featuring vehicles is not supported in the web adapter".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concat_u32_enums_to_str(enums: &[u32]) -> String {
|
||||
let mut out = String::new();
|
||||
for num in enums.iter() {
|
||||
if out.is_empty() {
|
||||
out += &num.to_string();
|
||||
} else {
|
||||
out += ",";
|
||||
out += &num.to_string();
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
4
rc_factory/src/web/mod.rs
Normal file
4
rc_factory/src/web/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! Web client adapter for the factory.
|
||||
//! Not to be confused with rc_factory_web, which provides the server part of this API.
|
||||
mod adapter;
|
||||
pub use adapter::WebAdapter;
|
||||
Reference in New Issue
Block a user