mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add proof-of-concept server intercom service through auth server
This commit is contained in:
@@ -18,6 +18,7 @@ chrono.workspace = true
|
||||
polariton_server.workspace = true
|
||||
tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util", "time" ] }
|
||||
async-trait.workspace = true
|
||||
futures.workspace = true
|
||||
rand.workspace = true
|
||||
num-quaternion.workspace = true
|
||||
|
||||
@@ -29,6 +30,7 @@ argon2 = { version = "0.5", features = [ "std" ] }
|
||||
# intercom
|
||||
sha2 = "0.10"
|
||||
reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", "charset" ] }
|
||||
reqwest-websocket = { version = "0.5", default-features = false, features = [ "json" ] }
|
||||
|
||||
oj_rc_database = { version = "*", path = "../rc_database" }
|
||||
oj_rc_factory = { version = "*", path = "../rc_factory" }
|
||||
|
||||
@@ -31,12 +31,19 @@ pub enum ChatOperation {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "built_in")]
|
||||
pub enum BuiltInChatOperation {
|
||||
Intercom(IntercomChatOperation),
|
||||
OnlineUsers,
|
||||
TotalUsers,
|
||||
Version,
|
||||
Help,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "intercom")]
|
||||
pub enum IntercomChatOperation {
|
||||
DevMessage,
|
||||
}
|
||||
|
||||
|
||||
fn default_pub_channs() -> Vec<String> {
|
||||
vec![
|
||||
|
||||
@@ -271,6 +271,8 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
||||
auto_signup: self.settings.server.auto_signup,
|
||||
queue_mode: super::QueueChangeMode::from_persist(self.settings.server.queue_mode.clone()),
|
||||
cdn_url: self.settings.server.cdn_url.trim_end_matches('/').to_owned(),
|
||||
auth_url: self.settings.server.auth_url.trim_matches('/').to_owned(),
|
||||
intercom_url: self.settings.server.intercom_url.trim_matches('/').to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,8 @@ pub struct ServerConfig {
|
||||
pub auto_signup: bool,
|
||||
pub queue_mode: QueueChangeMode,
|
||||
pub cdn_url: String,
|
||||
pub auth_url: String,
|
||||
pub intercom_url: String,
|
||||
}
|
||||
|
||||
pub enum QueueChangeMode {
|
||||
|
||||
@@ -30,7 +30,7 @@ mod settings;
|
||||
pub use settings::{Settings, QueueMode};
|
||||
|
||||
mod chat;
|
||||
pub use chat::{ChatConfig, ChatCommand, ChatOperation, BuiltInChatOperation};
|
||||
pub use chat::{ChatConfig, ChatCommand, ChatOperation, BuiltInChatOperation, IntercomChatOperation};
|
||||
|
||||
mod vehicle_factory;
|
||||
pub use vehicle_factory::{FactoryConfig, AdapterSettings, ArcFactorySettings};
|
||||
|
||||
@@ -78,6 +78,10 @@ pub struct ServerSettings {
|
||||
pub queue_mode: QueueMode,
|
||||
#[serde(default = "default_cdn_root_url")]
|
||||
pub cdn_url: String,
|
||||
#[serde(default = "default_auth_root_url")]
|
||||
pub auth_url: String,
|
||||
#[serde(default = "default_intercom_root_url")]
|
||||
pub intercom_url: String,
|
||||
#[serde(default = "default_feedback_url")]
|
||||
pub feedback_url: String,
|
||||
#[serde(default = "default_support_url")]
|
||||
@@ -104,6 +108,8 @@ fn default_server_conf() -> ServerSettings {
|
||||
auto_signup: false,
|
||||
queue_mode: QueueMode::Notify,
|
||||
cdn_url: default_cdn_root_url(),
|
||||
auth_url: default_auth_root_url(),
|
||||
intercom_url: default_intercom_root_url(),
|
||||
feedback_url: default_feedback_url(),
|
||||
support_url: default_support_url(),
|
||||
wiki_url: default_wiki_url(),
|
||||
@@ -114,6 +120,14 @@ fn default_cdn_root_url() -> String {
|
||||
"http://127.0.0.1:8010".to_owned()
|
||||
}
|
||||
|
||||
fn default_auth_root_url() -> String {
|
||||
"http://127.0.0.1:8001".to_owned() // mostly used for intercom
|
||||
}
|
||||
|
||||
fn default_intercom_root_url() -> String {
|
||||
"ws://127.0.0.1:8001".to_owned()
|
||||
}
|
||||
|
||||
fn default_feedback_url() -> String {
|
||||
"https://mstdn.ca/@ngram".to_owned()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ pub struct AccountProvider {
|
||||
fake_players: std::sync::Arc<Vec<crate::persist::config::FakePlayer>>,
|
||||
auto_signups: bool,
|
||||
cdn: std::sync::Arc<String>,
|
||||
auth: std::sync::Arc<String>,
|
||||
intercom: std::sync::Arc<String>,
|
||||
secret: std::sync::Arc<Vec<u8>>,
|
||||
db: std::sync::Arc<oj_rc_database::Database>,
|
||||
}
|
||||
@@ -29,6 +31,8 @@ impl AccountProvider {
|
||||
fake_players: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::fake_players(conf)),
|
||||
auto_signups: server_settings.auto_signup,
|
||||
cdn: std::sync::Arc::new(server_settings.cdn_url),
|
||||
auth: std::sync::Arc::new(server_settings.auth_url),
|
||||
intercom: std::sync::Arc::new(server_settings.intercom_url),
|
||||
secret: std::sync::Arc::new(secret),
|
||||
db: std::sync::Arc::new(db),
|
||||
})
|
||||
@@ -109,10 +113,12 @@ impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
||||
garage_upgrades: self.garage_upgrades.clone(),
|
||||
fake_players: self.fake_players.clone(),
|
||||
cdn: self.cdn.clone(),
|
||||
auth: self.auth.clone(),
|
||||
intercom: self.intercom.clone(),
|
||||
http_client: std::sync::Arc::new(reqwest::Client::new()),
|
||||
db: self.db.clone(),
|
||||
secret: self.secret.clone(),
|
||||
}))
|
||||
//Err("Unable to authenticate".to_string())
|
||||
}
|
||||
|
||||
async fn multiplayer_authenticate(&self, user: String) -> Result<Box<dyn super::User<C> + Send + Sync>, super::AuthError> {
|
||||
@@ -145,6 +151,9 @@ impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
||||
garage_upgrades: self.garage_upgrades.clone(),
|
||||
fake_players: self.fake_players.clone(),
|
||||
cdn: self.cdn.clone(),
|
||||
auth: self.auth.clone(),
|
||||
intercom: self.intercom.clone(),
|
||||
http_client: std::sync::Arc::new(reqwest::Client::new()),
|
||||
db: self.db.clone(),
|
||||
secret: self.secret.clone(),
|
||||
}))
|
||||
@@ -307,6 +316,9 @@ pub(super) struct UserData {
|
||||
pub(super) garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
||||
pub(super) fake_players: std::sync::Arc<Vec<crate::persist::config::FakePlayer>>,
|
||||
pub(super) cdn: std::sync::Arc<String>,
|
||||
pub(super) auth: std::sync::Arc<String>,
|
||||
pub(super) intercom: std::sync::Arc<String>,
|
||||
pub(super) http_client: std::sync::Arc<reqwest::Client>,
|
||||
pub(super) db: std::sync::Arc<oj_rc_database::Database>,
|
||||
pub(super) secret: std::sync::Arc<Vec<u8>>,
|
||||
}
|
||||
@@ -620,26 +632,6 @@ const UNEXPECTED_ERR: i16 = crate::data::error_codes::WebServicesError::Unexpect
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl <C: Clone> super::User<C> for UserData {
|
||||
fn public_id(&self) -> &'_ str {
|
||||
&self.account.public_id
|
||||
}
|
||||
|
||||
fn is_mod(&self) -> bool {
|
||||
self.perms.moderator
|
||||
}
|
||||
|
||||
fn is_admin(&self) -> bool {
|
||||
self.perms.administrator
|
||||
}
|
||||
|
||||
fn is_dev(&self) -> bool {
|
||||
self.perms.developer
|
||||
}
|
||||
|
||||
fn is_banned(&self) -> bool {
|
||||
self.perms.banned
|
||||
}
|
||||
|
||||
async fn unlocked_parts(&self) -> Vec<u32> {
|
||||
match self.db.user_aux_by_user_id_and_descriptor(self.account.id, oj_rc_database::schema::user_aux::Descriptor::UnlockedParts).await {
|
||||
Ok(Some(parts)) => {
|
||||
|
||||
@@ -2,6 +2,26 @@ use super::account_json::UserData;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl super::CommonUser for UserData {
|
||||
fn public_id(&self) -> &'_ str {
|
||||
&self.account.public_id
|
||||
}
|
||||
|
||||
fn is_mod(&self) -> bool {
|
||||
self.perms.moderator
|
||||
}
|
||||
|
||||
fn is_admin(&self) -> bool {
|
||||
self.perms.administrator
|
||||
}
|
||||
|
||||
fn is_dev(&self) -> bool {
|
||||
self.perms.developer
|
||||
}
|
||||
|
||||
fn is_banned(&self) -> bool {
|
||||
self.perms.banned
|
||||
}
|
||||
|
||||
async fn resolve_config_vehicle(&self, vehicle: &crate::persist::config::VehicleInfo, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, cpu_counter: &crate::cubes::CpuListParser) -> Result<super::ResolvedVehicle, polariton_server::operations::SimpleOpError> {
|
||||
self.resolve_vehicle(vehicle, factory, weapon_order, cpu_counter).await
|
||||
}
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
impl super::account_json::UserData {
|
||||
async fn listen_on_websocket<D: serde::de::DeserializeOwned>(&self, server_name: &str) -> Result<super::IntercomListener<D>, reqwest_websocket::Error> {
|
||||
use reqwest_websocket::RequestBuilderExt;
|
||||
let token = generate_token(format!("{}/{}", server_name, self.account.public_id).as_bytes(), &self.secret);
|
||||
let auth_header_val = format!("Internal {}", token);
|
||||
let url = format!("{}/intercom/{}/{}", self.intercom, server_name, self.account.public_id);
|
||||
log::debug!("Listening on websocket {}", url);
|
||||
let websocket = self.http_client.get(url)
|
||||
.header("Authorization", auth_header_val)
|
||||
.upgrade()
|
||||
.send()
|
||||
.await?
|
||||
.into_websocket()
|
||||
.await?;
|
||||
Ok(super::IntercomListener {
|
||||
websocket,
|
||||
_d: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn post_to_intercom<D: serde::Serialize>(&self, data: &D, server_name: &str, operation: &str) -> Result<(), reqwest::Error> {
|
||||
let path = format!("{}/{}/{}", server_name, self.account.public_id, operation);
|
||||
let token = generate_token(path.as_bytes(), &self.secret);
|
||||
let auth_header_val = format!("Internal {}", token);
|
||||
let url = format!("{}/intercom/{}", self.auth, path);
|
||||
log::debug!("Posting intercom message to {}", url);
|
||||
self.http_client.post(url)
|
||||
.header("Authorization", auth_header_val)
|
||||
.json(data)
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl super::IntercomUser for super::account_json::UserData {
|
||||
async fn save_custom_avatar(&self, image: Vec<u8>) -> Result<(), polariton_server::operations::SimpleOpError> {
|
||||
@@ -5,7 +42,7 @@ impl super::IntercomUser for super::account_json::UserData {
|
||||
let token = generate_token(self.account.public_id.as_bytes(), &self.secret);
|
||||
let auth_header_val = format!("Internal {}", token);
|
||||
let url = format!("{}/customavatar/Live/{}", self.cdn, self.account.public_id);
|
||||
if let Err(e) = reqwest::Client::new().post(url)
|
||||
if let Err(e) = self.http_client.post(url)
|
||||
.header("Authorization", auth_header_val)
|
||||
.body(image)
|
||||
.send()
|
||||
@@ -15,6 +52,42 @@ impl super::IntercomUser for super::account_json::UserData {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn webservice_listener(&self) -> Result<super::IntercomListener<IntercomWebServiceUserMessage>, polariton_server::operations::SimpleOpError> {
|
||||
self.listen_on_websocket(".oj_services").await
|
||||
.map_err(|e| polariton_server::operations::SimpleOpError::with_message(
|
||||
crate::data::error_codes::WebServicesError::PlatformFeatureNotAvailable as i16,
|
||||
e.to_string()
|
||||
))
|
||||
}
|
||||
|
||||
async fn show_dev_message(&self, msg: IntercomDevMessage, to: Vec<String>) {
|
||||
let data = IntercomWebServiceMessage {
|
||||
public_ids: to,
|
||||
data: IntercomWebServiceUserMessage::DevMessage(msg),
|
||||
};
|
||||
if let Err(e) = self.post_to_intercom(&data, ".oj_services", "messages").await {
|
||||
log::error!("Failed to send intercom dev message: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct IntercomWebServiceMessage {
|
||||
pub public_ids: Vec<String>,
|
||||
pub data: IntercomWebServiceUserMessage,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum IntercomWebServiceUserMessage {
|
||||
DevMessage(IntercomDevMessage),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct IntercomDevMessage {
|
||||
pub message: String,
|
||||
pub duration: u32,
|
||||
}
|
||||
|
||||
pub fn generate_token(salt: &[u8], key: &[u8]) -> String {
|
||||
|
||||
@@ -11,9 +11,9 @@ mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser};
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType, LobbyUser, GameDescriptor, PlayerLobbyDescriptor, MultiplayerUser, MultiplayerError, MultiplayerErrorCode, PlayerDescriptor, GameEventSetter, CurrentGameEvent, AuthError, IntercomUser, FakePlayers, ResolvedVehicle, CommonUser, IntercomListener};
|
||||
|
||||
mod intercom;
|
||||
pub mod intercom;
|
||||
pub use intercom::generate_token as generate_intercom_token;
|
||||
|
||||
mod multiplayer;
|
||||
|
||||
@@ -62,11 +62,6 @@ pub trait UserAuthenticator {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser + IntercomUser + CommonUser {
|
||||
fn public_id(&self) -> &'_ str;
|
||||
fn is_mod(&self) -> bool;
|
||||
fn is_admin(&self) -> bool;
|
||||
fn is_dev(&self) -> bool;
|
||||
fn is_banned(&self) -> bool;
|
||||
async fn unlocked_parts(&self) -> Vec<u32>;
|
||||
async fn selected_garage(&self) -> (String, u32);
|
||||
async fn select_garage(&self, slot: i32) -> Result<(), i16>;
|
||||
@@ -203,7 +198,7 @@ pub struct AvatarInfo {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ChatUser: CommonUser {
|
||||
pub trait ChatUser: CommonUser + IntercomUser {
|
||||
async fn subscribed_channels(&self) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
|
||||
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
@@ -340,6 +335,20 @@ pub trait MultiplayerUser: CommonUser {
|
||||
#[async_trait::async_trait]
|
||||
pub trait IntercomUser {
|
||||
async fn save_custom_avatar(&self, image: Vec<u8>) -> Result<(), polariton_server::operations::SimpleOpError>;
|
||||
async fn webservice_listener(&self) -> Result<IntercomListener<super::intercom::IntercomWebServiceUserMessage>, polariton_server::operations::SimpleOpError>;
|
||||
async fn show_dev_message(&self, msg: super::intercom::IntercomDevMessage, to: Vec<String>);
|
||||
}
|
||||
|
||||
pub struct IntercomListener<D: serde::de::DeserializeOwned> {
|
||||
pub(super) websocket: reqwest_websocket::WebSocket,
|
||||
pub(super) _d: std::marker::PhantomData<D>,
|
||||
}
|
||||
|
||||
impl <D: serde::de::DeserializeOwned> IntercomListener<D> {
|
||||
pub async fn listen(self) -> impl futures::Stream<Item=Result<D, reqwest_websocket::Error>> + Unpin {
|
||||
use futures::StreamExt;
|
||||
self.websocket.map(|msg| msg.and_then(|msg| msg.json()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResolvedVehicle {
|
||||
@@ -359,4 +368,9 @@ pub struct ResolvedVehicle {
|
||||
#[async_trait::async_trait]
|
||||
pub trait CommonUser: Send + Sync {
|
||||
async fn resolve_config_vehicle(&self, vehicle: &crate::persist::config::VehicleInfo, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, weapon_order: &crate::cubes::WeaponListParser, cpu_counter: &crate::cubes::CpuListParser) -> Result<ResolvedVehicle, polariton_server::operations::SimpleOpError>;
|
||||
fn public_id(&self) -> &'_ str;
|
||||
fn is_mod(&self) -> bool;
|
||||
fn is_admin(&self) -> bool;
|
||||
fn is_dev(&self) -> bool;
|
||||
fn is_banned(&self) -> bool;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user