mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Enable premium, add custom avatar saving support to complete #10
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2617,6 +2617,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"git-version",
|
"git-version",
|
||||||
"log",
|
"log",
|
||||||
|
"oj_rc_core",
|
||||||
"tokio",
|
"tokio",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
@@ -2700,8 +2701,10 @@ dependencies = [
|
|||||||
"polariton 0.3.0",
|
"polariton 0.3.0",
|
||||||
"polariton_server",
|
"polariton_server",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3499,6 +3502,7 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
|
|||||||
@@ -16,3 +16,5 @@ env_logger.workspace = true
|
|||||||
tokio = { version = "1.43", features = [ "rt-multi-thread" ] }
|
tokio = { version = "1.43", features = [ "rt-multi-thread" ] }
|
||||||
zip = "4"
|
zip = "4"
|
||||||
git-version.workspace = true
|
git-version.workspace = true
|
||||||
|
|
||||||
|
oj_rc_core = { version = "*", path = "../rc_core" }
|
||||||
|
|||||||
@@ -18,13 +18,16 @@ async fn index() -> impl Responder {
|
|||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let cli_args = cli::CliArgs::get();
|
let cli_args = cli::CliArgs::get();
|
||||||
let cli_args2 = cli_args.clone();
|
let cli_args2 = actix_web::web::Data::new(cli_args.clone());
|
||||||
|
let internal_auth = actix_web::web::Data::new(crate::robocraft::IntercomAuth::new(&cli_args.data_robocraft)?);
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(actix_web::web::Data::new(cli_args2.clone()))
|
.app_data(cli_args2.clone())
|
||||||
|
.app_data(internal_auth.clone())
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(robocraft::live_data::live_data_json)
|
.service(robocraft::live_data::live_data_json)
|
||||||
.service(robocraft::user_avatar::get)
|
.service(robocraft::user_avatar::get)
|
||||||
|
.service(robocraft::user_avatar::post)
|
||||||
.service(robocraft::clan_avatar::get)
|
.service(robocraft::clan_avatar::get)
|
||||||
.service(robocraft::brawl_data::get)
|
.service(robocraft::brawl_data::get)
|
||||||
.service(robocraft::campaign_data::get)
|
.service(robocraft::campaign_data::get)
|
||||||
|
|||||||
82
cdn/src/robocraft/internal_auth.rs
Normal file
82
cdn/src/robocraft/internal_auth.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
pub struct IntercomAuth {
|
||||||
|
key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntercomAuth {
|
||||||
|
pub fn new(data: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||||
|
let key = std::fs::read(data.as_ref().join(oj_rc_core::persist::user::TOKEN_SECRET_FILENAME))?;
|
||||||
|
Ok(Self {
|
||||||
|
key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_token(&self, received_token: &str, salt: &str) -> Result<(), IntercomOpError> {
|
||||||
|
let expected_token = oj_rc_core::persist::user::generate_intercom_token(salt.as_bytes(), &self.key);
|
||||||
|
if received_token.to_lowercase() == expected_token.to_lowercase() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(IntercomOpError::Unauthorized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(&self, req: &actix_web::HttpRequest, salt: &str) -> Result<(), IntercomOpError> {
|
||||||
|
if let Some(auth_header) = req.headers().get("Authorization") {
|
||||||
|
if let Ok(header_val) = auth_header.to_str() {
|
||||||
|
if let Some((bearer, token)) = header_val.split_once(" ") {
|
||||||
|
if bearer.to_lowercase() == "internal" || bearer.to_lowercase() == "bearer" {
|
||||||
|
self.validate_token(token, salt)?;
|
||||||
|
} else {
|
||||||
|
return Err(super::IntercomOpError::BadHeader);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(super::IntercomOpError::BadHeader);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(super::IntercomOpError::BadHeader);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(super::IntercomOpError::Unauthorized);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum IntercomOpError {
|
||||||
|
BadHeader,
|
||||||
|
Unauthorized,
|
||||||
|
Io(std::io::Error),
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for IntercomOpError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Intercom error variant {:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl actix_web::error::ResponseError for IntercomOpError {
|
||||||
|
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||||
|
match self {
|
||||||
|
Self::BadHeader => actix_web::http::StatusCode::BAD_REQUEST,
|
||||||
|
Self::Unauthorized => actix_web::http::StatusCode::FORBIDDEN,
|
||||||
|
Self::Io(_) => actix_web::http::StatusCode::INSUFFICIENT_STORAGE,
|
||||||
|
Self::Unknown => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
||||||
|
match self {
|
||||||
|
Self::Io(io_e) => {
|
||||||
|
actix_web::HttpResponse::new(self.status_code())
|
||||||
|
.set_body(format!("Intercom IO error: {}", io_e))
|
||||||
|
.map_into_boxed_body()
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
actix_web::HttpResponse::new(self.status_code()).set_body(self.to_string()).map_into_boxed_body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,5 +5,7 @@ pub mod brawl_data;
|
|||||||
pub mod campaign_data;
|
pub mod campaign_data;
|
||||||
pub mod factory;
|
pub mod factory;
|
||||||
pub mod favicon;
|
pub mod favicon;
|
||||||
|
mod internal_auth;
|
||||||
|
pub use internal_auth::{IntercomAuth, IntercomOpError};
|
||||||
|
|
||||||
pub(self) const DEFAULT_IMAGE: &str = "default.jpg";
|
pub(self) const DEFAULT_IMAGE: &str = "default.jpg";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use actix_web::{get, web::{Data, Path}, Responder};
|
use actix_web::{get, post, web::{Data, Path, Bytes}, Responder};
|
||||||
|
|
||||||
#[get("/customavatar/Live/{name}")]
|
#[get("/customavatar/Live/{name}")]
|
||||||
pub async fn get(cli: Data<crate::cli::CliArgs>, name: Path<String>) -> impl Responder {
|
pub async fn get(cli: Data<crate::cli::CliArgs>, name: Path<String>) -> impl Responder {
|
||||||
@@ -11,3 +11,12 @@ pub async fn get(cli: Data<crate::cli::CliArgs>, name: Path<String>) -> impl Res
|
|||||||
actix_files::NamedFile::open_async(std::path::PathBuf::from(&cli.assets_robocraft).join(super::DEFAULT_IMAGE)).await
|
actix_files::NamedFile::open_async(std::path::PathBuf::from(&cli.assets_robocraft).join(super::DEFAULT_IMAGE)).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/customavatar/Live/{name}")]
|
||||||
|
pub async fn post(cli: Data<crate::cli::CliArgs>, auth: Data<crate::robocraft::IntercomAuth>, name: Path<String>, body: Bytes, req: actix_web::HttpRequest) -> Result<actix_web::HttpResponse, super::IntercomOpError> {
|
||||||
|
auth.validate(&req, &name)?;
|
||||||
|
let path = std::path::PathBuf::from(&cli.data_robocraft).join("customavatars").join(format!("{}.jpg", *name));
|
||||||
|
log::debug!("Saving customavatar for {} to {}: {}B", name, path.display(), body.len());
|
||||||
|
std::fs::write(path, &body).map_err(super::IntercomOpError::Io)?;
|
||||||
|
Ok(actix_web::HttpResponse::NoContent().finish())
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,5 +25,9 @@ libfj.workspace = true
|
|||||||
jsonwebtoken = "9"
|
jsonwebtoken = "9"
|
||||||
argon2 = { version = "0.5", features = [ "std" ] }
|
argon2 = { version = "0.5", features = [ "std" ] }
|
||||||
|
|
||||||
|
# intercom
|
||||||
|
sha2 = "0.10"
|
||||||
|
reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls", "charset" ] }
|
||||||
|
|
||||||
oj_rc_database = { version = "*", path = "../rc_database" }
|
oj_rc_database = { version = "*", path = "../rc_database" }
|
||||||
oj_rc_factory = { version = "*", path = "../rc_factory" }
|
oj_rc_factory = { version = "*", path = "../rc_factory" }
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
database: self.settings.server.database.clone(),
|
database: self.settings.server.database.clone(),
|
||||||
auto_signup: self.settings.server.auto_signup,
|
auto_signup: self.settings.server.auto_signup,
|
||||||
queue_mode: super::QueueChangeMode::from_persist(self.settings.server.queue_mode.clone()),
|
queue_mode: super::QueueChangeMode::from_persist(self.settings.server.queue_mode.clone()),
|
||||||
|
cdn_url: self.settings.server.cdn_url.trim_end_matches('/').to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ pub struct ServerConfig {
|
|||||||
pub database: String,
|
pub database: String,
|
||||||
pub auto_signup: bool,
|
pub auto_signup: bool,
|
||||||
pub queue_mode: QueueChangeMode,
|
pub queue_mode: QueueChangeMode,
|
||||||
|
pub cdn_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum QueueChangeMode {
|
pub enum QueueChangeMode {
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ pub struct ServerSettings {
|
|||||||
pub auto_signup: bool,
|
pub auto_signup: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub queue_mode: QueueMode,
|
pub queue_mode: QueueMode,
|
||||||
|
#[serde(default = "default_cdn_root_url")]
|
||||||
|
pub cdn_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
@@ -95,5 +97,10 @@ fn default_server_conf() -> ServerSettings {
|
|||||||
database: default_db_conn(),
|
database: default_db_conn(),
|
||||||
auto_signup: false,
|
auto_signup: false,
|
||||||
queue_mode: QueueMode::Notify,
|
queue_mode: QueueMode::Notify,
|
||||||
|
cdn_url: default_cdn_root_url(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_cdn_root_url() -> String {
|
||||||
|
"http://127.0.0.1:8010".to_owned()
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ pub struct AccountProvider {
|
|||||||
cubes: std::sync::Arc<Vec<u32>>,
|
cubes: std::sync::Arc<Vec<u32>>,
|
||||||
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
||||||
auto_signups: bool,
|
auto_signups: bool,
|
||||||
secret: Vec<u8>,
|
cdn: std::sync::Arc<String>,
|
||||||
|
secret: std::sync::Arc<Vec<u8>>,
|
||||||
db: std::sync::Arc<oj_rc_database::Database>,
|
db: std::sync::Arc<oj_rc_database::Database>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +23,8 @@ impl AccountProvider {
|
|||||||
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(conf)),
|
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(conf)),
|
||||||
garage_upgrades: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::garage_upgrades(conf)),
|
garage_upgrades: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::garage_upgrades(conf)),
|
||||||
auto_signups: server_settings.auto_signup,
|
auto_signups: server_settings.auto_signup,
|
||||||
secret: std::fs::read(&token_path)?,
|
cdn: std::sync::Arc::new(server_settings.cdn_url),
|
||||||
|
secret: std::sync::Arc::new(std::fs::read(&token_path)?),
|
||||||
db: std::sync::Arc::new(db),
|
db: std::sync::Arc::new(db),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -100,7 +102,9 @@ impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
|||||||
perms: user_perms,
|
perms: user_perms,
|
||||||
cubes: self.cubes.clone(),
|
cubes: self.cubes.clone(),
|
||||||
garage_upgrades: self.garage_upgrades.clone(),
|
garage_upgrades: self.garage_upgrades.clone(),
|
||||||
|
cdn: self.cdn.clone(),
|
||||||
db: self.db.clone(),
|
db: self.db.clone(),
|
||||||
|
secret: self.secret.clone(),
|
||||||
}))
|
}))
|
||||||
//Err("Unable to authenticate".to_string())
|
//Err("Unable to authenticate".to_string())
|
||||||
}
|
}
|
||||||
@@ -133,7 +137,9 @@ impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
|||||||
perms: user_perms,
|
perms: user_perms,
|
||||||
cubes: self.cubes.clone(),
|
cubes: self.cubes.clone(),
|
||||||
garage_upgrades: self.garage_upgrades.clone(),
|
garage_upgrades: self.garage_upgrades.clone(),
|
||||||
|
cdn: self.cdn.clone(),
|
||||||
db: self.db.clone(),
|
db: self.db.clone(),
|
||||||
|
secret: self.secret.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,13 +293,14 @@ impl super::UserAuthenticator for AccountProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub(super) struct UserData {
|
||||||
struct UserData {
|
pub(super) account: oj_rc_database::schema::user::Model,
|
||||||
account: oj_rc_database::schema::user::Model,
|
pub(super) perms: oj_rc_database::schema::permissions::Model,
|
||||||
perms: oj_rc_database::schema::permissions::Model,
|
pub(super) cubes: std::sync::Arc<Vec<u32>>,
|
||||||
cubes: std::sync::Arc<Vec<u32>>,
|
pub(super) garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
||||||
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
pub(super) cdn: std::sync::Arc<String>,
|
||||||
db: std::sync::Arc<oj_rc_database::Database>,
|
pub(super) db: std::sync::Arc<oj_rc_database::Database>,
|
||||||
|
pub(super) secret: std::sync::Arc<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserData {
|
impl UserData {
|
||||||
@@ -1365,7 +1372,6 @@ impl super::LobbyUser for UserData {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl super::MultiplayerUser for UserData {
|
impl super::MultiplayerUser for UserData {
|
||||||
// TODO
|
|
||||||
fn user_id(&self) -> i32 {
|
fn user_id(&self) -> i32 {
|
||||||
self.account.id
|
self.account.id
|
||||||
}
|
}
|
||||||
|
|||||||
28
rc_core/src/persist/user/intercom.rs
Normal file
28
rc_core/src/persist/user/intercom.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#[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> {
|
||||||
|
// seems to always be jpg
|
||||||
|
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)
|
||||||
|
.header("Authorization", auth_header_val)
|
||||||
|
.body(image)
|
||||||
|
.send()
|
||||||
|
.await {
|
||||||
|
log::error!("Failed to update custom avatar for {} ({}): {}", self.account.public_id, self.account.id, e);
|
||||||
|
return Err((crate::data::error_codes::WebServicesError::UnexpectedError as i16).into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_token(salt: &[u8], key: &[u8]) -> String {
|
||||||
|
use sha2::{Digest, Sha512};
|
||||||
|
let mut hasher = Sha512::new();
|
||||||
|
hasher.update(salt);
|
||||||
|
hasher.update(key);
|
||||||
|
let token_bytes = hasher.finalize();
|
||||||
|
hex::encode(&token_bytes[..])
|
||||||
|
}
|
||||||
|
|
||||||
@@ -11,7 +11,10 @@ mod inventory;
|
|||||||
pub use inventory::UnlockedParts;
|
pub use inventory::UnlockedParts;
|
||||||
|
|
||||||
mod traits;
|
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};
|
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};
|
||||||
|
|
||||||
|
mod intercom;
|
||||||
|
pub use intercom::generate_token as generate_intercom_token;
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ pub trait UserAuthenticator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser {
|
pub trait User<C>: ChatUser + LobbyUser + MultiplayerUser + IntercomUser {
|
||||||
fn public_id(&self) -> &'_ str;
|
fn public_id(&self) -> &'_ str;
|
||||||
fn is_mod(&self) -> bool;
|
fn is_mod(&self) -> bool;
|
||||||
fn is_admin(&self) -> bool;
|
fn is_admin(&self) -> bool;
|
||||||
@@ -328,3 +328,8 @@ pub trait MultiplayerUser {
|
|||||||
async fn complete_game(&self, guid: &str) -> Result<(), MultiplayerError>;
|
async fn complete_game(&self, guid: &str) -> Result<(), MultiplayerError>;
|
||||||
async fn game_info(&self, guid: &str) -> Result<Option<GameDescriptor>, MultiplayerError>;
|
async fn game_info(&self, guid: &str) -> Result<Option<GameDescriptor>, MultiplayerError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait IntercomUser {
|
||||||
|
async fn save_custom_avatar(&self, image: Vec<u8>) -> Result<(), polariton_server::operations::SimpleOpError>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ pub(super) fn custom_avatar_upload_handler() -> CustomAvatarHandler {
|
|||||||
CustomAvatarHandler
|
CustomAvatarHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_save(params: ParameterTable<()>, _user: &crate::UserTy) -> Result<ParameterTable, i16> {
|
async fn do_save(params: ParameterTable<()>, user: &crate::UserTy) -> Result<ParameterTable, i16> {
|
||||||
let mut params = params.to_dict();
|
let mut params = params.to_dict();
|
||||||
//let user_info = user.user()?;
|
//let user_info = user.user()?;
|
||||||
if let Some(Typed::Bytes(image)) = params.remove(&IMG_PARAM_KEY) {
|
if let Some(Typed::Bytes(image)) = params.remove(&IMG_PARAM_KEY) {
|
||||||
if let Some(Typed::Int(format)) = params.remove(&FORMAT_PARAM_KEY) {
|
if let Some(Typed::Int(format)) = params.remove(&FORMAT_PARAM_KEY) {
|
||||||
log::debug!("Got custom avatar ({}B) with format {}", image.vec.len(), format);
|
log::debug!("Got custom avatar ({}B) with format {}", image.vec.len(), format);
|
||||||
|
if format != 0 {
|
||||||
|
log::warn!("Got non-jpg format {} for custom avatar, this was assumed to be impossible", format);
|
||||||
|
}
|
||||||
|
user.user()?.save_custom_avatar(image.vec).await?;
|
||||||
// TODO actually save image
|
// TODO actually save image
|
||||||
/*let info = oj_rc_core::persist::user::AvatarInfo {
|
/*let info = oj_rc_core::persist::user::AvatarInfo {
|
||||||
avatar_id: 0,
|
avatar_id: 0,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub(super) fn premium_remaining_provider() -> SimpleFunc<15, crate::UserTy, impl
|
|||||||
params.insert(HOURS_PARAM_KEY, Typed::Int(0));
|
params.insert(HOURS_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(MINUTES_PARAM_KEY, Typed::Int(0));
|
params.insert(MINUTES_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(SECONDS_PARAM_KEY, Typed::Int(0));
|
params.insert(SECONDS_PARAM_KEY, Typed::Int(0));
|
||||||
params.insert(LIFETIME_PARAM_KEY, Typed::Bool(false));
|
params.insert(LIFETIME_PARAM_KEY, Typed::Bool(true));
|
||||||
Ok(params.into())
|
Ok(params.into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user