2024-01-30 17:19:34 +01:00
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
2023-08-26 19:41:00 +02:00
|
|
|
//! Instance and ChorusUser objects.
|
|
|
|
|
2023-07-09 18:38:02 +02:00
|
|
|
use std::collections::HashMap;
|
2023-06-11 13:52:31 +02:00
|
|
|
use std::fmt;
|
2023-08-28 18:12:17 +02:00
|
|
|
|
2023-08-12 19:31:31 +02:00
|
|
|
use std::sync::{Arc, RwLock};
|
2023-06-11 13:52:31 +02:00
|
|
|
|
2023-06-20 22:41:31 +02:00
|
|
|
use reqwest::Client;
|
2023-05-26 12:50:16 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2023-07-09 18:38:02 +02:00
|
|
|
use crate::errors::ChorusResult;
|
2024-06-23 17:23:13 +02:00
|
|
|
use crate::gateway::{Gateway, GatewayHandle, GatewayOptions};
|
2023-07-09 18:38:02 +02:00
|
|
|
use crate::ratelimiter::ChorusRequest;
|
|
|
|
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
2023-12-03 21:44:08 +01:00
|
|
|
use crate::types::{
|
2024-04-28 14:15:57 +02:00
|
|
|
GeneralConfiguration, Limit, LimitType, LimitsConfiguration, Shared, User, UserSettings,
|
2023-12-03 21:44:08 +01:00
|
|
|
};
|
2023-11-19 19:12:29 +01:00
|
|
|
use crate::UrlBundle;
|
2023-04-16 22:16:22 +02:00
|
|
|
|
2023-12-02 17:35:47 +01:00
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
2023-07-10 16:59:00 +02:00
|
|
|
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
|
2024-01-20 13:15:13 +01:00
|
|
|
///
|
2023-07-10 16:59:00 +02:00
|
|
|
/// If `limits_information` is `None`, then the instance will not be rate limited.
|
2023-05-07 12:39:04 +02:00
|
|
|
pub struct Instance {
|
2023-06-20 02:59:18 +02:00
|
|
|
pub urls: UrlBundle,
|
2023-05-25 23:09:18 +02:00
|
|
|
pub instance_info: GeneralConfiguration,
|
2023-07-09 18:38:02 +02:00
|
|
|
pub limits_information: Option<LimitsInformation>,
|
2023-12-02 17:35:47 +01:00
|
|
|
#[serde(skip)]
|
2023-06-20 22:41:31 +02:00
|
|
|
pub client: Client,
|
2024-06-23 17:23:13 +02:00
|
|
|
#[serde(skip)]
|
|
|
|
pub gateway_options: GatewayOptions,
|
2023-04-16 23:03:12 +02:00
|
|
|
}
|
|
|
|
|
2023-12-02 17:35:47 +01:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq)]
|
2023-07-09 18:38:02 +02:00
|
|
|
pub struct LimitsInformation {
|
|
|
|
pub ratelimits: HashMap<LimitType, Limit>,
|
|
|
|
pub configuration: RateLimits,
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:35:47 +01:00
|
|
|
impl std::hash::Hash for LimitsInformation {
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
for (k, v) in self.ratelimits.iter() {
|
|
|
|
k.hash(state);
|
|
|
|
v.hash(state);
|
|
|
|
}
|
|
|
|
self.configuration.hash(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-18 23:04:35 +02:00
|
|
|
#[cfg(not(tarpaulin_include))]
|
2023-12-02 17:35:47 +01:00
|
|
|
impl PartialEq for LimitsInformation {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.ratelimits.iter().eq(other.ratelimits.iter())
|
|
|
|
&& self.configuration == other.configuration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-07 12:39:04 +02:00
|
|
|
impl Instance {
|
2024-01-20 13:15:13 +01:00
|
|
|
pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
|
|
|
|
if self.limits_information.is_some() {
|
|
|
|
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
|
|
|
///
|
|
|
|
/// To create an Instance from one singular url, use [`Instance::new()`].
|
|
|
|
pub async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> {
|
2023-12-03 21:44:08 +01:00
|
|
|
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
|
|
|
|
let limit_information;
|
|
|
|
|
|
|
|
if let Some(limits_configuration) = is_limited {
|
|
|
|
let limits = ChorusRequest::limits_config_to_hashmap(&limits_configuration.rate);
|
|
|
|
limit_information = Some(LimitsInformation {
|
2023-11-15 20:26:47 +01:00
|
|
|
ratelimits: limits,
|
2023-12-03 21:44:08 +01:00
|
|
|
configuration: limits_configuration.rate,
|
2023-07-09 18:38:02 +02:00
|
|
|
});
|
|
|
|
} else {
|
2023-12-03 21:44:08 +01:00
|
|
|
limit_information = None
|
2023-07-09 18:38:02 +02:00
|
|
|
}
|
2023-04-19 20:41:33 +02:00
|
|
|
let mut instance = Instance {
|
2023-04-24 19:49:26 +02:00
|
|
|
urls: urls.clone(),
|
2023-06-19 10:27:32 +02:00
|
|
|
// Will be overwritten in the next step
|
|
|
|
instance_info: GeneralConfiguration::default(),
|
2023-12-03 21:44:08 +01:00
|
|
|
limits_information: limit_information,
|
2023-06-20 22:41:31 +02:00
|
|
|
client: Client::new(),
|
2024-06-23 17:23:13 +02:00
|
|
|
gateway_options: GatewayOptions::default(),
|
2023-04-19 20:41:33 +02:00
|
|
|
};
|
2023-05-25 23:09:18 +02:00
|
|
|
instance.instance_info = match instance.general_configuration_schema().await {
|
2023-04-19 20:41:33 +02:00
|
|
|
Ok(schema) => schema,
|
2023-04-21 23:20:23 +02:00
|
|
|
Err(e) => {
|
2023-07-09 18:38:02 +02:00
|
|
|
log::warn!("Could not get instance configuration schema: {}", e);
|
|
|
|
GeneralConfiguration::default()
|
2023-04-21 23:20:23 +02:00
|
|
|
}
|
2023-04-19 20:41:33 +02:00
|
|
|
};
|
|
|
|
Ok(instance)
|
|
|
|
}
|
2023-12-03 13:37:32 +01:00
|
|
|
|
2023-12-03 13:39:23 +01:00
|
|
|
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
|
2023-12-03 13:37:32 +01:00
|
|
|
///
|
2024-01-20 13:15:13 +01:00
|
|
|
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
2023-12-15 00:10:33 +01:00
|
|
|
pub async fn new(root_url: &str) -> ChorusResult<Instance> {
|
2023-12-03 13:39:23 +01:00
|
|
|
let urls = UrlBundle::from_root_url(root_url).await?;
|
2023-12-15 00:10:33 +01:00
|
|
|
Instance::from_url_bundle(urls).await
|
2023-12-03 21:44:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
|
|
|
let api_url = UrlBundle::parse_url(api_url.to_string());
|
|
|
|
let client = Client::new();
|
|
|
|
let request = client
|
|
|
|
.get(format!("{}/policies/instance/limits", &api_url))
|
|
|
|
.header(http::header::ACCEPT, "application/json")
|
|
|
|
.build()?;
|
|
|
|
let resp = match client.execute(request).await {
|
|
|
|
Ok(response) => response,
|
|
|
|
Err(_) => return Ok(None),
|
|
|
|
};
|
|
|
|
match resp.json::<LimitsConfiguration>().await {
|
|
|
|
Ok(limits) => Ok(Some(limits)),
|
|
|
|
Err(_) => Ok(None),
|
|
|
|
}
|
2023-12-03 13:37:32 +01:00
|
|
|
}
|
2024-06-23 17:23:13 +02:00
|
|
|
|
|
|
|
/// Sets the [`GatewayOptions`] the instance will use when spawning new connections.
|
|
|
|
///
|
|
|
|
/// These options are used on the gateways created when logging in and registering.
|
|
|
|
pub fn set_gateway_options(&mut self, options: GatewayOptions) {
|
|
|
|
self.gateway_options = options;
|
|
|
|
}
|
2023-04-19 20:41:33 +02:00
|
|
|
}
|
|
|
|
|
2023-05-26 12:50:16 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
2023-04-21 23:20:23 +02:00
|
|
|
pub struct Token {
|
|
|
|
pub token: String,
|
2023-04-19 20:41:33 +02:00
|
|
|
}
|
|
|
|
|
2023-04-21 23:20:23 +02:00
|
|
|
impl fmt::Display for Token {
|
2023-04-19 20:41:33 +02:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2023-04-21 23:20:23 +02:00
|
|
|
write!(f, "{}", self.token)
|
2023-04-19 20:41:33 +02:00
|
|
|
}
|
2023-04-16 22:16:22 +02:00
|
|
|
}
|
2023-04-17 21:31:15 +02:00
|
|
|
|
2023-08-29 00:05:16 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2023-08-24 21:09:23 +02:00
|
|
|
/// A ChorusUser is a representation of an authenticated user on an [Instance].
|
2023-07-29 10:23:04 +02:00
|
|
|
/// It is used for most authenticated actions on a Spacebar server.
|
|
|
|
/// It also has its own [Gateway] connection.
|
2023-08-24 21:06:28 +02:00
|
|
|
pub struct ChorusUser {
|
2024-01-23 19:07:23 +01:00
|
|
|
pub belongs_to: Shared<Instance>,
|
2023-05-25 23:09:18 +02:00
|
|
|
pub token: String,
|
2023-07-09 18:38:02 +02:00
|
|
|
pub limits: Option<HashMap<LimitType, Limit>>,
|
2024-01-23 19:07:23 +01:00
|
|
|
pub settings: Shared<UserSettings>,
|
|
|
|
pub object: Shared<User>,
|
2023-08-29 14:24:32 +02:00
|
|
|
pub gateway: GatewayHandle,
|
2023-05-25 23:09:18 +02:00
|
|
|
}
|
|
|
|
|
2023-08-24 21:06:28 +02:00
|
|
|
impl ChorusUser {
|
2023-05-25 23:09:18 +02:00
|
|
|
pub fn token(&self) -> String {
|
|
|
|
self.token.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_token(&mut self, token: String) {
|
|
|
|
self.token = token;
|
|
|
|
}
|
|
|
|
|
2023-08-24 21:09:23 +02:00
|
|
|
/// Creates a new [ChorusUser] from existing data.
|
2023-07-29 10:23:04 +02:00
|
|
|
///
|
|
|
|
/// # Notes
|
2024-01-31 22:27:53 +01:00
|
|
|
/// This isn't the preferred way to create a ChorusUser.
|
2023-07-29 10:23:04 +02:00
|
|
|
/// See [Instance::login_account] and [Instance::register_account] instead.
|
2023-05-25 23:09:18 +02:00
|
|
|
pub fn new(
|
2024-01-23 19:07:23 +01:00
|
|
|
belongs_to: Shared<Instance>,
|
2023-05-25 23:09:18 +02:00
|
|
|
token: String,
|
2023-07-09 18:38:02 +02:00
|
|
|
limits: Option<HashMap<LimitType, Limit>>,
|
2024-01-23 19:07:23 +01:00
|
|
|
settings: Shared<UserSettings>,
|
|
|
|
object: Shared<User>,
|
2023-08-29 14:24:32 +02:00
|
|
|
gateway: GatewayHandle,
|
2023-08-24 21:06:28 +02:00
|
|
|
) -> ChorusUser {
|
|
|
|
ChorusUser {
|
2023-05-25 23:09:18 +02:00
|
|
|
belongs_to,
|
|
|
|
token,
|
|
|
|
limits,
|
|
|
|
settings,
|
|
|
|
object,
|
2023-07-24 19:13:53 +02:00
|
|
|
gateway,
|
2023-05-25 23:09:18 +02:00
|
|
|
}
|
|
|
|
}
|
2023-07-09 18:38:02 +02:00
|
|
|
|
|
|
|
/// Creates a new 'shell' of a user. The user does not exist as an object, and exists so that you have
|
2023-08-24 21:09:23 +02:00
|
|
|
/// a ChorusUser object to make Rate Limited requests with. This is useful in scenarios like
|
2023-07-09 18:38:02 +02:00
|
|
|
/// registering or logging in to the Instance, where you do not yet have a User object, but still
|
2023-07-24 19:13:53 +02:00
|
|
|
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
|
|
|
/// first.
|
2024-01-23 19:07:23 +01:00
|
|
|
pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> ChorusUser {
|
2023-08-12 19:31:31 +02:00
|
|
|
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
|
|
|
let object = Arc::new(RwLock::new(User::default()));
|
2023-08-28 18:12:17 +02:00
|
|
|
let wss_url = instance.read().unwrap().urls.wss.clone();
|
2023-07-24 19:13:53 +02:00
|
|
|
// Dummy gateway object
|
2024-06-23 17:23:13 +02:00
|
|
|
let gateway = Gateway::spawn(wss_url, GatewayOptions::default())
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2023-08-24 21:06:28 +02:00
|
|
|
ChorusUser {
|
2023-07-09 18:38:02 +02:00
|
|
|
token,
|
2023-07-24 19:13:53 +02:00
|
|
|
belongs_to: instance.clone(),
|
2023-07-09 18:38:02 +02:00
|
|
|
limits: instance
|
2023-08-28 18:12:17 +02:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2023-07-09 18:38:02 +02:00
|
|
|
.limits_information
|
|
|
|
.as_ref()
|
|
|
|
.map(|info| info.ratelimits.clone()),
|
|
|
|
settings,
|
|
|
|
object,
|
2023-07-24 19:13:53 +02:00
|
|
|
gateway,
|
2023-07-09 18:38:02 +02:00
|
|
|
}
|
|
|
|
}
|
2023-05-25 23:09:18 +02:00
|
|
|
}
|