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]]
|
[[package]]
|
||||||
name = "libfj"
|
name = "libfj"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0f55afe6b910429afcd02d7810760e07e8489ad662f7df0e938a6154fe413d41"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ members = [
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
actix-web = { version = "4", default-features = false, features = [ "macros", "compress-brotli", "compress-gzip", "compress-zstd", "ws"] }
|
actix-web = { version = "4", default-features = false, features = [ "macros", "compress-brotli", "compress-gzip", "compress-zstd", "ws"] }
|
||||||
actix-files = "0.6"
|
actix-files = "0.6"
|
||||||
#libfj = { version = "0.9", path = "../libfj" }
|
libfj = { version = "0.10", path = "../libfj" }
|
||||||
libfj = { version = "0.9" }
|
#libfj = { version = "0.9" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
clap = { version = "4.5", features = [ "derive" ] }
|
clap = { version = "4.5", features = [ "derive" ] }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub enum Factory {
|
pub enum Factory {
|
||||||
Arc(oj_rc_factory::arc::ArcAdapter),
|
Arc(oj_rc_factory::arc::ArcAdapter),
|
||||||
Primary(oj_rc_database::FactoryDatabase),
|
Primary(oj_rc_database::FactoryDatabase),
|
||||||
|
Web(oj_rc_factory::web::WebAdapter),
|
||||||
Custom(Box<dyn oj_rc_factory::VehicleFactoryAdapter + Send + Sync + 'static>),
|
Custom(Box<dyn oj_rc_factory::VehicleFactoryAdapter + Send + Sync + 'static>),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
@@ -11,6 +12,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.vehicle(id).await,
|
Self::Arc(x) => x.vehicle(id).await,
|
||||||
Self::Primary(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::Custom(x) => x.vehicle(id).await,
|
||||||
Self::None => Ok(None),
|
Self::None => Ok(None),
|
||||||
}
|
}
|
||||||
@@ -20,6 +22,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.list(query).await,
|
Self::Arc(x) => x.list(query).await,
|
||||||
Self::Primary(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::Custom(x) => x.list(query).await,
|
||||||
Self::None => Ok(Vec::default()),
|
Self::None => Ok(Vec::default()),
|
||||||
}
|
}
|
||||||
@@ -29,6 +32,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.upload(vehicle).await,
|
Self::Arc(x) => x.upload(vehicle).await,
|
||||||
Self::Primary(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::Custom(x) => x.upload(vehicle).await,
|
||||||
Self::None => Ok(oj_rc_factory::VehicleThumbnailInfo {
|
Self::None => Ok(oj_rc_factory::VehicleThumbnailInfo {
|
||||||
id: i32::MIN,
|
id: i32::MIN,
|
||||||
@@ -42,6 +46,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
Self::Arc(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||||
Self::Primary(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::Custom(x) => x.rate_vehicle(id, combat, cosmetic).await,
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -51,6 +56,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.purchase(id).await,
|
Self::Arc(x) => x.purchase(id).await,
|
||||||
Self::Primary(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::Custom(x) => x.purchase(id).await,
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -60,6 +66,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
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::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::Custom(x) => x.update_vehicle(id, cube_data, colour_data).await,
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -69,6 +76,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.remove_vehicle(id, user_id).await,
|
Self::Arc(x) => x.remove_vehicle(id, user_id).await,
|
||||||
Self::Primary(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::Custom(x) => x.remove_vehicle(id, user_id).await,
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -78,6 +86,7 @@ impl oj_rc_factory::VehicleFactoryAdapter for Factory {
|
|||||||
match self {
|
match self {
|
||||||
Self::Arc(x) => x.set_featured(id, is_featured).await,
|
Self::Arc(x) => x.set_featured(id, is_featured).await,
|
||||||
Self::Primary(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::Custom(x) => x.set_featured(id, is_featured).await,
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
}
|
}
|
||||||
@@ -89,6 +98,7 @@ impl Factory {
|
|||||||
Ok(match &conf.adapter {
|
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::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::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,
|
crate::persist::AdapterSettings::None => Self::None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ pub enum AdapterSettings {
|
|||||||
Arc(ArcFactorySettings),
|
Arc(ArcFactorySettings),
|
||||||
#[serde(alias = "integrated")]
|
#[serde(alias = "integrated")]
|
||||||
BuiltIn,
|
BuiltIn,
|
||||||
|
#[serde(alias = "online")]
|
||||||
|
Web(WebFactorySettings),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,3 +44,8 @@ pub struct ArcFactorySettings {
|
|||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct WebFactorySettings {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod arc;
|
pub mod arc;
|
||||||
|
pub mod web;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{VehicleFactoryAdapter, VehicleInfo, VehicleQueryInfo, VehicleUploadInfo, VehicleThumbnailInfo};
|
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