mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
feat: Implement models and meta routes. Add empty files for routes still to be implemented.
This commit is contained in:
@@ -197,6 +197,8 @@ admin_api_enabled = false
|
||||
mass_mentions_send_notifications = true
|
||||
# Can role/everyone pings be used at all
|
||||
mass_mentions_enabled = true
|
||||
# Show the admin api in the OpenAPI spec
|
||||
admin_api_show_spec = false
|
||||
|
||||
[features.limits]
|
||||
|
||||
|
||||
@@ -355,6 +355,7 @@ pub struct Features {
|
||||
pub mass_mentions_send_notifications: bool,
|
||||
pub mass_mentions_enabled: bool,
|
||||
pub admin_api_enabled: bool,
|
||||
pub admin_api_show_spec: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub advanced: FeaturesAdvanced,
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
use axum::{extract::FromRequestParts, http::request::Parts};
|
||||
|
||||
use revolt_result::{create_error, Error, Result};
|
||||
|
||||
use crate::{AdminMachineToken, Database};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl FromRequestParts<Database> for AdminMachineToken {
|
||||
type Rejection = Error;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, db: &Database) -> Result<AdminMachineToken> {
|
||||
if let Some(Ok(on_behalf_of)) = parts
|
||||
.headers
|
||||
.get("x-admin-on-behalf-of")
|
||||
.map(|v| v.to_str())
|
||||
{
|
||||
if let Some(Ok(token)) = parts.headers.get("x-admin-machine").map(|v| v.to_str()) {
|
||||
let config = revolt_config::config().await;
|
||||
let token = token.to_string();
|
||||
if config.api.security.admin_keys.contains(&token) {
|
||||
let resp: AdminMachineToken;
|
||||
// shitty email check
|
||||
if on_behalf_of.contains("@") {
|
||||
resp = AdminMachineToken::new_from_email(on_behalf_of, db).await?
|
||||
} else {
|
||||
resp = AdminMachineToken::new_from_id(on_behalf_of, db).await?
|
||||
}
|
||||
return Ok(resp);
|
||||
} else {
|
||||
return Err(create_error!(InvalidCredentials));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(create_error!(NotAuthenticated))
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
#[cfg(feature = "axum-impl")]
|
||||
mod axum;
|
||||
mod models;
|
||||
mod ops;
|
||||
#[cfg(feature = "rocket-impl")]
|
||||
mod rocket;
|
||||
mod schema;
|
||||
|
||||
#[cfg(feature = "axum-impl")]
|
||||
pub use self::axum::*;
|
||||
#[cfg(feature = "rocket-impl")]
|
||||
pub use self::rocket::*;
|
||||
#[cfg(feature = "rocket-impl")]
|
||||
pub use self::schema::*;
|
||||
pub use models::*;
|
||||
pub use ops::*;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::{AdminUser, Database};
|
||||
@@ -20,10 +21,45 @@ auto_derived! {
|
||||
/// Placeholder field.
|
||||
pub on_behalf_of: AdminUser
|
||||
}
|
||||
|
||||
pub enum AdminAuthorization {
|
||||
AdminUser(AdminUser),
|
||||
AdminMachine(AdminMachineToken),
|
||||
}
|
||||
}
|
||||
|
||||
impl AdminToken {
|
||||
pub fn new(user_id: &str, expiry: Timestamp) -> AdminToken {
|
||||
let id = ulid::Ulid::new().to_string();
|
||||
let token = nanoid::nanoid!(64);
|
||||
AdminToken {
|
||||
id,
|
||||
user_id: user_id.to_string(),
|
||||
token,
|
||||
expiry: expiry.format_short().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AdminMachineToken {
|
||||
pub async fn new_from_email(email: &str, db: &Database) -> Result<AdminMachineToken> {
|
||||
println!("{}", email);
|
||||
if email == "example@example.com" {
|
||||
// This is basically just a workaround to make the first user.
|
||||
let user_count = db.admin_user_count().await?;
|
||||
println!("{}", user_count);
|
||||
if user_count == 0 {
|
||||
return Ok(AdminMachineToken {
|
||||
on_behalf_of: AdminUser {
|
||||
id: "00".to_string(),
|
||||
platform_user_id: "".to_string(),
|
||||
email: "example@example.com".to_string(),
|
||||
active: true,
|
||||
permissions: u64::MAX,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
let user = db.admin_user_fetch_email(email).await?;
|
||||
Ok(AdminMachineToken { on_behalf_of: user })
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@ pub trait AbstractAdminTokens: Sync + Send {
|
||||
async fn admin_token_revoke(&self, token_id: &str) -> Result<()>;
|
||||
|
||||
async fn admin_token_authenticate(&self, token: &str) -> Result<AdminToken>;
|
||||
|
||||
async fn admin_token_fetch(&self, id: &str) -> Result<AdminToken>;
|
||||
}
|
||||
|
||||
@@ -27,4 +27,8 @@ impl AbstractAdminTokens for MongoDb {
|
||||
query!(self, find_one, COL, doc! {"token": token})?
|
||||
.ok_or_else(|| create_error!(InvalidCredentials))
|
||||
}
|
||||
|
||||
async fn admin_token_fetch(&self, id: &str) -> Result<AdminToken> {
|
||||
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,13 @@ impl AbstractAdminTokens for ReferenceDb {
|
||||
|
||||
Ok(result.ok_or_else(|| create_error!(NotFound))?)
|
||||
}
|
||||
|
||||
async fn admin_token_fetch(&self, id: &str) -> Result<AdminToken> {
|
||||
let admin_tokens = self.admin_tokens.lock().await;
|
||||
let result = admin_tokens.iter().find(|tok| tok.0 == id);
|
||||
|
||||
Ok(result
|
||||
.map(|t| t.1.clone())
|
||||
.ok_or_else(|| create_error!(NotFound))?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_config::{capture_internal_error, report_error};
|
||||
use revolt_result::Result;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{self, FromRequest, Outcome, Request};
|
||||
|
||||
use crate::{AdminMachineToken, Database};
|
||||
use crate::{AdminAuthorization, AdminMachineToken, AdminUser, Database};
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for AdminMachineToken {
|
||||
@@ -50,3 +52,95 @@ impl<'r> FromRequest<'r> for AdminMachineToken {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for AdminAuthorization {
|
||||
type Error = authifier::Error;
|
||||
|
||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
let machine: &Option<AdminMachineToken> = request
|
||||
.local_cache_async(async {
|
||||
let db = request.rocket().state::<Database>().expect("`Database`");
|
||||
|
||||
if let Some(token) = request
|
||||
.headers()
|
||||
.get("x-admin-machine")
|
||||
.next()
|
||||
.map(|x| x.to_string())
|
||||
{
|
||||
if let Some(on_behalf_of) = request
|
||||
.headers()
|
||||
.get("x-admin-on-behalf-of")
|
||||
.next()
|
||||
.map(|x| x.to_string())
|
||||
{
|
||||
let config = revolt_config::config().await;
|
||||
let token = token.to_string();
|
||||
if config.api.security.admin_keys.contains(&token) {
|
||||
let resp: Result<AdminMachineToken> = if on_behalf_of.contains("@") {
|
||||
AdminMachineToken::new_from_email(&on_behalf_of, db).await
|
||||
} else {
|
||||
AdminMachineToken::new_from_id(&on_behalf_of, db).await
|
||||
};
|
||||
if let Ok(resp) = resp {
|
||||
return Some(resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Some(machine) = machine {
|
||||
if !machine.on_behalf_of.active {
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::LockedOut))
|
||||
} else {
|
||||
Outcome::Success(AdminAuthorization::AdminMachine(machine.clone()))
|
||||
}
|
||||
} else {
|
||||
let user: &Option<AdminUser> = request
|
||||
.local_cache_async(async {
|
||||
let db = request.rocket().state::<Database>().expect("`Database`");
|
||||
|
||||
let token = request
|
||||
.headers()
|
||||
.get("x-admin-user")
|
||||
.next()
|
||||
.map(|x| x.to_string());
|
||||
|
||||
if let Some(token) = token {
|
||||
if let Ok(admin_token) = db.admin_token_authenticate(&token).await {
|
||||
if let Some(expiry) = Timestamp::parse(&admin_token.expiry) {
|
||||
if expiry < Timestamp::now_utc() {
|
||||
if let Err(e) = db.admin_token_revoke(&admin_token.id).await {
|
||||
capture_internal_error!(e);
|
||||
}
|
||||
return None;
|
||||
} else if let Ok(user) =
|
||||
db.admin_user_fetch(&admin_token.user_id).await
|
||||
{
|
||||
if !user.active {
|
||||
return None;
|
||||
}
|
||||
return Some(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Some(user) = user {
|
||||
if !user.active {
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::LockedOut))
|
||||
} else {
|
||||
Outcome::Success(AdminAuthorization::AdminUser(user.clone()))
|
||||
}
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::InvalidCredentials))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
crates/core/database/src/models/admin_tokens/schema.rs
Normal file
59
crates/core/database/src/models/admin_tokens/schema.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData};
|
||||
use revolt_rocket_okapi::{
|
||||
gen::OpenApiGenerator,
|
||||
request::{OpenApiFromRequest, RequestHeaderInput},
|
||||
};
|
||||
|
||||
use crate::{AdminAuthorization, AdminMachineToken};
|
||||
|
||||
impl OpenApiFromRequest<'_> for AdminAuthorization {
|
||||
fn from_request_input(
|
||||
_gen: &mut OpenApiGenerator,
|
||||
_name: String,
|
||||
_required: bool,
|
||||
) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
|
||||
let mut requirements = schemars::Map::new();
|
||||
requirements.insert("Admin Token".to_owned(), vec![]);
|
||||
|
||||
Ok(RequestHeaderInput::Security(
|
||||
"Admin Token".to_owned(),
|
||||
SecurityScheme {
|
||||
data: SecuritySchemeData::ApiKey {
|
||||
name: "x-admin-user".to_owned(),
|
||||
location: "header".to_owned(),
|
||||
},
|
||||
description: Some("Used to authenticate as an admin user.
|
||||
Can instead use an x-admin-machine token with an x-on-behalf-of containing the userid or email of
|
||||
the user the machine is performing the action for.".to_owned()),
|
||||
extensions: schemars::Map::new(),
|
||||
},
|
||||
requirements,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenApiFromRequest<'_> for AdminMachineToken {
|
||||
fn from_request_input(
|
||||
_gen: &mut OpenApiGenerator,
|
||||
_name: String,
|
||||
_required: bool,
|
||||
) -> revolt_rocket_okapi::Result<RequestHeaderInput> {
|
||||
let mut requirements = schemars::Map::new();
|
||||
requirements.insert("Admin Machine Token".to_owned(), vec![]);
|
||||
|
||||
Ok(RequestHeaderInput::Security(
|
||||
"Admin Machine Token".to_owned(),
|
||||
SecurityScheme {
|
||||
data: SecuritySchemeData::ApiKey {
|
||||
name: "x-admin-machine".to_owned(),
|
||||
location: "header".to_owned(),
|
||||
},
|
||||
description: Some("Used by machines to authenticate on behalf of a user.
|
||||
Machines are trusted devices that authenticate as other users via providing the x-on-behalf-of header
|
||||
with an admin user's ID or email.".to_owned()),
|
||||
extensions: schemars::Map::new(),
|
||||
},
|
||||
requirements,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
use axum::{extract::FromRequestParts, http::request::Parts};
|
||||
|
||||
use revolt_result::{create_error, Error, Result};
|
||||
|
||||
use crate::{AdminUser, Database};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl FromRequestParts<Database> for AdminUser {
|
||||
type Rejection = Error;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, db: &Database) -> Result<AdminUser> {
|
||||
if let Some(Ok(token)) = parts.headers.get("x-admin-user").map(|v| v.to_str()) {
|
||||
let session = db.admin_token_authenticate(token).await?;
|
||||
db.admin_user_fetch(&session.user_id).await
|
||||
} else {
|
||||
Err(create_error!(NotAuthenticated))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
#[cfg(feature = "axum-impl")]
|
||||
mod axum;
|
||||
mod models;
|
||||
mod ops;
|
||||
#[cfg(feature = "rocket-impl")]
|
||||
mod rocket;
|
||||
|
||||
#[cfg(feature = "axum-impl")]
|
||||
pub use self::axum::*;
|
||||
#[cfg(feature = "rocket-impl")]
|
||||
pub use self::rocket::*;
|
||||
pub use models::*;
|
||||
|
||||
@@ -39,3 +39,31 @@ impl AdminUser {
|
||||
return db.admin_user_fetch_email(email).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AdminUserPermissionFlagsValue(pub u64);
|
||||
|
||||
impl AdminUserPermissionFlagsValue {
|
||||
pub fn has(&self, flag: revolt_models::v0::AdminUserPermissionFlags) -> bool {
|
||||
self.has_value(flag as u64)
|
||||
}
|
||||
pub fn has_value(&self, bit: u64) -> bool {
|
||||
let mask = 1 << bit;
|
||||
self.0 & mask == mask
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&mut self,
|
||||
flag: revolt_models::v0::AdminUserPermissionFlags,
|
||||
toggle: bool,
|
||||
) -> &mut Self {
|
||||
self.set_value(flag as u64, toggle)
|
||||
}
|
||||
pub fn set_value(&mut self, bit: u64, toggle: bool) -> &mut Self {
|
||||
if toggle {
|
||||
self.0 |= 1 << bit;
|
||||
} else {
|
||||
self.0 &= !(1 << bit);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,6 @@ pub trait AbstractAdminUsers: Sync + Send {
|
||||
async fn admin_user_fetch_email(&self, email: &str) -> Result<AdminUser>;
|
||||
|
||||
async fn admin_user_list(&self) -> Result<Vec<AdminUser>>;
|
||||
|
||||
async fn admin_user_count(&self) -> Result<u16>;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,8 @@ impl AbstractAdminUsers for MongoDb {
|
||||
async fn admin_user_list(&self) -> Result<Vec<AdminUser>> {
|
||||
query!(self, find, COL, doc! {})
|
||||
}
|
||||
|
||||
async fn admin_user_count(&self) -> Result<u16> {
|
||||
query!(self, count_documents, COL, doc! {}).map(|resp| resp as u16)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,4 +49,8 @@ impl AbstractAdminUsers for ReferenceDb {
|
||||
let admin_users = self.admin_users.lock().await;
|
||||
Ok(admin_users.iter().map(|(_, u)| u).cloned().collect())
|
||||
}
|
||||
|
||||
async fn admin_user_count(&self) -> Result<u16> {
|
||||
Ok(self.admin_users.lock().await.len() as u16)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,13 @@ impl<'r> FromRequest<'r> for AdminUser {
|
||||
.await;
|
||||
|
||||
if let Some(user) = user {
|
||||
Outcome::Success(user.clone())
|
||||
if !user.active {
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::LockedOut))
|
||||
} else {
|
||||
Outcome::Success(user.clone())
|
||||
}
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::InvalidSession))
|
||||
Outcome::Error((Status::Unauthorized, authifier::Error::InvalidCredentials))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +617,7 @@ impl From<crate::Report> for Report {
|
||||
additional_context: value.additional_context,
|
||||
status: value.status,
|
||||
notes: value.notes,
|
||||
case_id: value.case_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1387,3 +1388,72 @@ impl From<FieldsMessage> for crate::FieldsMessage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::AdminUser> for AdminUser {
|
||||
fn from(value: crate::AdminUser) -> Self {
|
||||
AdminUser {
|
||||
id: value.id,
|
||||
platform_user_id: value.platform_user_id,
|
||||
email: value.email,
|
||||
active: value.active,
|
||||
permissions: value.permissions,
|
||||
revolt_user: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AdminUser> for crate::AdminUser {
|
||||
fn from(value: AdminUser) -> Self {
|
||||
crate::AdminUser {
|
||||
id: value.id,
|
||||
platform_user_id: value.platform_user_id,
|
||||
email: value.email,
|
||||
active: value.active,
|
||||
permissions: value.permissions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AdminUserEdit> for crate::PartialAdminUser {
|
||||
fn from(value: AdminUserEdit) -> Self {
|
||||
crate::PartialAdminUser {
|
||||
id: None,
|
||||
platform_user_id: value.platform_user_id,
|
||||
email: value.email,
|
||||
active: value.active,
|
||||
permissions: value.permissions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AdminToken> for crate::AdminToken {
|
||||
fn from(value: AdminToken) -> Self {
|
||||
crate::AdminToken {
|
||||
id: value.id,
|
||||
user_id: value.user_id,
|
||||
token: value.token,
|
||||
expiry: value.expiry.format_short().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::AdminToken> for AdminToken {
|
||||
fn from(value: crate::AdminToken) -> Self {
|
||||
AdminToken {
|
||||
id: value.id,
|
||||
user_id: value.user_id,
|
||||
token: value.token,
|
||||
expiry: Timestamp::parse(&value.expiry).unwrap(), // if it's invalid it shouldn't have made it to this point.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::AdminObjectNote> for AdminObjectNote {
|
||||
fn from(value: crate::AdminObjectNote) -> Self {
|
||||
AdminObjectNote {
|
||||
edited_at: Timestamp::parse(&value.edited_at).unwrap(), // if it's invalid it shouldn't have made it to this point.
|
||||
last_edited_by_id: value.last_edited_by_id,
|
||||
content: value.content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ use schemars::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User, Webhook,
|
||||
AdminToken, Bot, Channel, Database, Emoji, Invite, Member, Message, Server, ServerBan, User,
|
||||
Webhook,
|
||||
};
|
||||
|
||||
/// Reference to some object in the database
|
||||
@@ -25,6 +26,11 @@ impl<'a> Reference<'a> {
|
||||
Reference { id }
|
||||
}
|
||||
|
||||
/// Fetch Admin Token from Ref
|
||||
pub async fn as_admin_token(&self, db: &Database) -> Result<AdminToken> {
|
||||
db.admin_token_fetch(&self.id).await
|
||||
}
|
||||
|
||||
/// Fetch ban from Ref
|
||||
pub async fn as_ban(&self, db: &Database, server: &str) -> Result<ServerBan> {
|
||||
db.fetch_ban(server, self.id).await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
||||
use crate::v0::Report;
|
||||
use crate::v0::{Report, User};
|
||||
|
||||
auto_derived! {
|
||||
#[derive(Default)]
|
||||
@@ -211,9 +211,6 @@ auto_derived! {
|
||||
|
||||
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||
pub struct AdminTokenCreate {
|
||||
/// the user who this token is for
|
||||
#[cfg_attr(feature = "serde", serde(rename="user"))]
|
||||
pub user_id: String,
|
||||
/// The expiry timestamp for this token, in iso6801. Max 30 days from current time.
|
||||
pub expiry: Timestamp,
|
||||
}
|
||||
@@ -229,6 +226,9 @@ auto_derived! {
|
||||
pub active: bool,
|
||||
/// The permissions of the user
|
||||
pub permissions: u64,
|
||||
/// The Revolt user attached to this user. Use this for data like names and avatars.
|
||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||
pub revolt_user: Option<User>
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||
@@ -258,4 +258,31 @@ auto_derived! {
|
||||
/// The permissions of the user
|
||||
pub permissions: Option<u64>,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum AdminUserPermissionFlags {
|
||||
ObjectNotes = 0,
|
||||
|
||||
ManageAdminUsers = 1,
|
||||
CreateTokens = 2,
|
||||
|
||||
ViewUsers = 3,
|
||||
ManageUsers = 4,
|
||||
ManageUsersSenstiveInfo = 5,
|
||||
|
||||
ViewServers = 6,
|
||||
ManageServers = 7,
|
||||
ViewDMChannels = 8,
|
||||
ManageDMChannels = 9,
|
||||
|
||||
ViewCases = 10,
|
||||
ManageOwnCases = 11,
|
||||
ManageOtherCases = 12,
|
||||
|
||||
ViewReports = 13,
|
||||
|
||||
Search = 14,
|
||||
ManageNotes = 15,
|
||||
Discover = 16,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// This should allow resetting of all factors or individual ones.
|
||||
1
crates/delta/src/routes/admin/accounts/mod.rs
Normal file
1
crates/delta/src/routes/admin/accounts/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
//close/reopen a case
|
||||
0
crates/delta/src/routes/admin/cases/actions/mod.rs
Normal file
0
crates/delta/src/routes/admin/cases/actions/mod.rs
Normal file
1
crates/delta/src/routes/admin/cases/fetch/case_get.rs
Normal file
1
crates/delta/src/routes/admin/cases/fetch/case_get.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
0
crates/delta/src/routes/admin/cases/fetch/mod.rs
Normal file
0
crates/delta/src/routes/admin/cases/fetch/mod.rs
Normal file
2
crates/delta/src/routes/admin/cases/mod.rs
Normal file
2
crates/delta/src/routes/admin/cases/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod actions;
|
||||
mod fetch;
|
||||
0
crates/delta/src/routes/admin/channels/fetch/mod.rs
Normal file
0
crates/delta/src/routes/admin/channels/fetch/mod.rs
Normal file
2
crates/delta/src/routes/admin/channels/mod.rs
Normal file
2
crates/delta/src/routes/admin/channels/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod actions;
|
||||
mod fetch;
|
||||
0
crates/delta/src/routes/admin/messages/delete.rs
Normal file
0
crates/delta/src/routes/admin/messages/delete.rs
Normal file
38
crates/delta/src/routes/admin/meta/create_token.rs
Normal file
38
crates/delta/src/routes/admin/meta/create_token.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use iso8601_timestamp::{Duration, Timestamp};
|
||||
use revolt_database::{AdminMachineToken, AdminToken, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
use crate::routes::admin::util::user_has_permission;
|
||||
|
||||
/// Create a token for your account. Must have the CreateTokens permission
|
||||
#[openapi(tag = "Admin")]
|
||||
#[post("/tokens", data = "<body>")]
|
||||
pub async fn admin_create_token(
|
||||
db: &State<Database>,
|
||||
auth: AdminMachineToken,
|
||||
body: Json<v0::AdminTokenCreate>,
|
||||
) -> Result<Json<v0::AdminToken>> {
|
||||
if !user_has_permission(
|
||||
&auth.on_behalf_of,
|
||||
v0::AdminUserPermissionFlags::CreateTokens,
|
||||
) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
if body.expiry > Timestamp::now_utc() + Duration::days(30) {
|
||||
return Err(create_error!(FailedValidation {
|
||||
error: "The expiry is more than 30 days away.".to_string()
|
||||
}));
|
||||
}
|
||||
|
||||
let token = AdminToken::new(&auth.on_behalf_of.id, body.expiry);
|
||||
db.admin_token_create(token.clone()).await?;
|
||||
Ok(Json(v0::AdminToken {
|
||||
id: token.id,
|
||||
user_id: token.user_id,
|
||||
token: token.token,
|
||||
expiry: body.expiry,
|
||||
}))
|
||||
}
|
||||
26
crates/delta/src/routes/admin/meta/create_user.rs
Normal file
26
crates/delta/src/routes/admin/meta/create_user.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use revolt_database::{AdminAuthorization, AdminUser, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
use crate::routes::admin::util::{flatten_authorized_user, user_has_permission};
|
||||
|
||||
/// Edit an admin user. Requires ManageAdminUsers flag.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[post("/users", data = "<body>")]
|
||||
pub async fn admin_create_user(
|
||||
db: &State<Database>,
|
||||
auth: AdminAuthorization,
|
||||
body: Json<v0::AdminUserCreate>,
|
||||
) -> Result<Json<v0::AdminUser>> {
|
||||
let user = flatten_authorized_user(&auth);
|
||||
if !user_has_permission(user, v0::AdminUserPermissionFlags::ManageAdminUsers) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
// TODO: technically there's a privilege escalation here since anyone with the manageAdminUsers permission can assign whatever permissions they want.
|
||||
// But this is built with the assumption that people with ManageAdminUsers are already privileged.
|
||||
let user = AdminUser::new(&body.email, &body.platform_user_id, body.permissions);
|
||||
db.admin_user_insert(user.clone()).await?;
|
||||
Ok(Json(user.into()))
|
||||
}
|
||||
27
crates/delta/src/routes/admin/meta/edit_user.rs
Normal file
27
crates/delta/src/routes/admin/meta/edit_user.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use revolt_database::{util::reference::Reference, AdminAuthorization, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
use rocket_empty::EmptyResponse;
|
||||
|
||||
use crate::routes::admin::util::{flatten_authorized_user, user_has_permission};
|
||||
|
||||
/// Edit an admin user. Requires ManageAdminUsers flag.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[patch("/users/<target>", data = "<body>")]
|
||||
pub async fn admin_edit_user(
|
||||
db: &State<Database>,
|
||||
auth: AdminAuthorization,
|
||||
target: Reference,
|
||||
body: Json<v0::AdminUserEdit>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let user = flatten_authorized_user(&auth);
|
||||
if !user_has_permission(user, v0::AdminUserPermissionFlags::ManageAdminUsers) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
// TODO: technically there's a privilege escalation here since anyone with the manageAdminUsers permission can assign whatever permissions they want.
|
||||
// But this is built with the assumption that people with ManageAdminUsers are already privileged.
|
||||
db.admin_user_update(&target.id, body.0.into()).await?;
|
||||
Ok(EmptyResponse)
|
||||
}
|
||||
29
crates/delta/src/routes/admin/meta/fetch_users.rs
Normal file
29
crates/delta/src/routes/admin/meta/fetch_users.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use revolt_database::{AdminAuthorization, Database};
|
||||
use revolt_models::v0::AdminUser;
|
||||
use revolt_result::Result;
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// Get a list of admin users. Any active user may use this endpoint.
|
||||
/// Typically the client should cache this data.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[get("/users")]
|
||||
pub async fn admin_fetch_users(
|
||||
db: &State<Database>,
|
||||
auth: AdminAuthorization,
|
||||
) -> Result<Json<Vec<AdminUser>>> {
|
||||
let users = db.admin_user_list().await?;
|
||||
let userids: Vec<String> = users.iter().map(|u| u.platform_user_id.clone()).collect();
|
||||
let revolt_users = db.fetch_users(&userids).await?;
|
||||
|
||||
let mut resp = vec![];
|
||||
for admin_user in users {
|
||||
let mut user = AdminUser::from(admin_user);
|
||||
user.revolt_user = match revolt_users.iter().find(|ru| ru.id == user.id) {
|
||||
Some(some_user) => Some(some_user.clone().into_self(false).await),
|
||||
None => None,
|
||||
};
|
||||
resp.push(user);
|
||||
}
|
||||
|
||||
Ok(Json(resp))
|
||||
}
|
||||
5
crates/delta/src/routes/admin/meta/mod.rs
Normal file
5
crates/delta/src/routes/admin/meta/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod create_token;
|
||||
pub mod create_user;
|
||||
pub mod edit_user;
|
||||
pub mod fetch_users;
|
||||
pub mod revoke_token;
|
||||
39
crates/delta/src/routes/admin/meta/revoke_token.rs
Normal file
39
crates/delta/src/routes/admin/meta/revoke_token.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use revolt_database::{util::reference::Reference, AdminMachineToken, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::State;
|
||||
use rocket_empty::EmptyResponse;
|
||||
|
||||
use crate::routes::admin::util::user_has_permission;
|
||||
|
||||
/// Revoke a token. Must be your own, or you must have the ManageAdminUsers permission to revoke other's tokens.
|
||||
/// You must have the CreateTokens permission.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[delete("/tokens/<token>")]
|
||||
pub async fn admin_revoke_token(
|
||||
db: &State<Database>,
|
||||
auth: AdminMachineToken,
|
||||
token: Reference,
|
||||
) -> Result<EmptyResponse> {
|
||||
if !user_has_permission(
|
||||
&auth.on_behalf_of,
|
||||
v0::AdminUserPermissionFlags::CreateTokens,
|
||||
) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
let token = token.as_admin_token(db).await?;
|
||||
|
||||
// only own user and those with ManageAdminUsers can revoke a token
|
||||
if token.user_id != auth.on_behalf_of.id
|
||||
&& !user_has_permission(
|
||||
&auth.on_behalf_of,
|
||||
v0::AdminUserPermissionFlags::ManageAdminUsers,
|
||||
)
|
||||
{
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
db.admin_token_revoke(&token.id).await?;
|
||||
Ok(EmptyResponse)
|
||||
}
|
||||
26
crates/delta/src/routes/admin/mod.rs
Normal file
26
crates/delta/src/routes/admin/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||
use rocket::Route;
|
||||
|
||||
mod accounts;
|
||||
mod cases;
|
||||
mod meta;
|
||||
mod reports;
|
||||
mod roles;
|
||||
mod search;
|
||||
mod users;
|
||||
|
||||
mod object_edit_note;
|
||||
mod object_get_note;
|
||||
mod util;
|
||||
|
||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
object_edit_note::admin_object_edit_note,
|
||||
object_get_note::admin_object_get_note,
|
||||
meta::create_user::admin_create_user,
|
||||
meta::edit_user::admin_edit_user,
|
||||
meta::fetch_users::admin_fetch_users,
|
||||
meta::create_token::admin_create_token,
|
||||
meta::revoke_token::admin_revoke_token
|
||||
]
|
||||
}
|
||||
28
crates/delta/src/routes/admin/object_edit_note.rs
Normal file
28
crates/delta/src/routes/admin/object_edit_note.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use revolt_database::{util::reference::Reference, AdminAuthorization, AdminObjectNote, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
use rocket_empty::EmptyResponse;
|
||||
|
||||
use crate::routes::admin::util::{flatten_authorized_user, user_has_permission};
|
||||
|
||||
/// Edit an object note. Requires ObjectNotes permission.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[post("/notes/<object>", data = "<body>")]
|
||||
pub async fn admin_object_edit_note(
|
||||
db: &State<Database>,
|
||||
auth: AdminAuthorization,
|
||||
object: Reference,
|
||||
body: Json<v0::AdminObjectNoteEdit>,
|
||||
) -> Result<EmptyResponse> {
|
||||
let user = flatten_authorized_user(&auth);
|
||||
if !user_has_permission(user, v0::AdminUserPermissionFlags::ObjectNotes) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
// Unfortunately due to how our system works, we can't limit users to editing objects they have permissions for (eg users, servers, etc.).
|
||||
// Hence it being tied to its own permission
|
||||
db.admin_note_update(AdminObjectNote::new(&object.id, &user.id, &body.content))
|
||||
.await?;
|
||||
Ok(EmptyResponse)
|
||||
}
|
||||
24
crates/delta/src/routes/admin/object_get_note.rs
Normal file
24
crates/delta/src/routes/admin/object_get_note.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use revolt_database::{util::reference::Reference, AdminAuthorization, Database};
|
||||
use revolt_models::v0::{self};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
use crate::routes::admin::util::{flatten_authorized_user, user_has_permission};
|
||||
|
||||
/// Edit an object note. Requires ObjectNotes permission.
|
||||
#[openapi(tag = "Admin")]
|
||||
#[get("/notes/<object>")]
|
||||
pub async fn admin_object_get_note(
|
||||
db: &State<Database>,
|
||||
auth: AdminAuthorization,
|
||||
object: Reference,
|
||||
) -> Result<Json<v0::AdminObjectNote>> {
|
||||
let user = flatten_authorized_user(&auth);
|
||||
if !user_has_permission(user, v0::AdminUserPermissionFlags::ObjectNotes) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
// Unfortunately due to how our system works, we can't limit users to editing objects they have permissions for (eg users, servers, etc.).
|
||||
// Hence it being tied to its own permission
|
||||
Ok(Json(db.admin_note_fetch(&object.id).await?.into()))
|
||||
}
|
||||
0
crates/delta/src/routes/admin/reports/fetch/mod.rs
Normal file
0
crates/delta/src/routes/admin/reports/fetch/mod.rs
Normal file
2
crates/delta/src/routes/admin/reports/mod.rs
Normal file
2
crates/delta/src/routes/admin/reports/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod actions;
|
||||
mod fetch;
|
||||
3
crates/delta/src/routes/admin/roles/mod.rs
Normal file
3
crates/delta/src/routes/admin/roles/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod role_create;
|
||||
mod role_delete;
|
||||
mod role_edit;
|
||||
0
crates/delta/src/routes/admin/roles/role_create.rs
Normal file
0
crates/delta/src/routes/admin/roles/role_create.rs
Normal file
0
crates/delta/src/routes/admin/roles/role_delete.rs
Normal file
0
crates/delta/src/routes/admin/roles/role_delete.rs
Normal file
0
crates/delta/src/routes/admin/roles/role_edit.rs
Normal file
0
crates/delta/src/routes/admin/roles/role_edit.rs
Normal file
1
crates/delta/src/routes/admin/search/mod.rs
Normal file
1
crates/delta/src/routes/admin/search/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
mod search_everything;
|
||||
0
crates/delta/src/routes/admin/servers/fetch/mod.rs
Normal file
0
crates/delta/src/routes/admin/servers/fetch/mod.rs
Normal file
2
crates/delta/src/routes/admin/servers/mod.rs
Normal file
2
crates/delta/src/routes/admin/servers/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod actions;
|
||||
mod fetch;
|
||||
0
crates/delta/src/routes/admin/users/actions/mod.rs
Normal file
0
crates/delta/src/routes/admin/users/actions/mod.rs
Normal file
0
crates/delta/src/routes/admin/users/fetch/mod.rs
Normal file
0
crates/delta/src/routes/admin/users/fetch/mod.rs
Normal file
2
crates/delta/src/routes/admin/users/mod.rs
Normal file
2
crates/delta/src/routes/admin/users/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod actions;
|
||||
mod fetch;
|
||||
15
crates/delta/src/routes/admin/util.rs
Normal file
15
crates/delta/src/routes/admin/util.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use revolt_database::{AdminAuthorization, AdminUser, AdminUserPermissionFlagsValue};
|
||||
use revolt_models::v0::AdminUserPermissionFlags;
|
||||
|
||||
pub fn user_has_permission(user: &AdminUser, permission: AdminUserPermissionFlags) -> bool {
|
||||
let flags = AdminUserPermissionFlagsValue(user.permissions);
|
||||
|
||||
flags.has(permission)
|
||||
}
|
||||
|
||||
pub fn flatten_authorized_user<'a>(auth: &'a AdminAuthorization) -> &'a AdminUser {
|
||||
match auth {
|
||||
AdminAuthorization::AdminUser(admin_user) => &admin_user,
|
||||
AdminAuthorization::AdminMachine(admin_machine_token) => &admin_machine_token.on_behalf_of,
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ pub use rocket::http::Status;
|
||||
pub use rocket::response::Redirect;
|
||||
use rocket::{Build, Rocket};
|
||||
|
||||
mod admin;
|
||||
mod bots;
|
||||
mod channels;
|
||||
mod customisation;
|
||||
@@ -21,89 +22,79 @@ mod webhooks;
|
||||
pub fn mount(config: Settings, mut rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
let settings = OpenApiSettings::default();
|
||||
|
||||
if config.features.webhooks_enabled {
|
||||
mount_endpoints_and_merged_docs! {
|
||||
rocket, "/".to_owned(), settings,
|
||||
"/" => (vec![], custom_openapi_spec()),
|
||||
"" => openapi_get_routes_spec![root::root],
|
||||
"/users" => users::routes(),
|
||||
"/bots" => bots::routes(),
|
||||
"/channels" => channels::routes(),
|
||||
"/servers" => servers::routes(),
|
||||
"/invites" => invites::routes(),
|
||||
"/custom" => customisation::routes(),
|
||||
"/safety" => safety::routes(),
|
||||
"/auth/account" => rocket_authifier::routes::account::routes(),
|
||||
"/auth/session" => rocket_authifier::routes::session::routes(),
|
||||
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
|
||||
"/onboard" => onboard::routes(),
|
||||
"/policy" => policy::routes(),
|
||||
"/push" => push::routes(),
|
||||
"/sync" => sync::routes(),
|
||||
"/webhooks" => webhooks::routes()
|
||||
};
|
||||
} else {
|
||||
mount_endpoints_and_merged_docs! {
|
||||
rocket, "/".to_owned(), settings,
|
||||
"/" => (vec![], custom_openapi_spec()),
|
||||
"" => openapi_get_routes_spec![root::root],
|
||||
"/users" => users::routes(),
|
||||
"/bots" => bots::routes(),
|
||||
"/channels" => channels::routes(),
|
||||
"/servers" => servers::routes(),
|
||||
"/invites" => invites::routes(),
|
||||
"/custom" => customisation::routes(),
|
||||
"/safety" => safety::routes(),
|
||||
"/auth/account" => rocket_authifier::routes::account::routes(),
|
||||
"/auth/session" => rocket_authifier::routes::session::routes(),
|
||||
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
|
||||
"/onboard" => onboard::routes(),
|
||||
"/policy" => policy::routes(),
|
||||
"/push" => push::routes(),
|
||||
"/sync" => sync::routes()
|
||||
};
|
||||
let mut openapi_list: Vec<(_, revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi)> =
|
||||
Vec::new();
|
||||
|
||||
let routes = vec![
|
||||
("/", (vec![], custom_openapi_spec()), true, true),
|
||||
("/", openapi_get_routes_spec![root::root], true, true),
|
||||
("/users", users::routes(), true, true),
|
||||
("/bots", bots::routes(), true, true),
|
||||
("/channels", channels::routes(), true, true),
|
||||
("/servers", servers::routes(), true, true),
|
||||
("/invites", invites::routes(), true, true),
|
||||
("/custom", customisation::routes(), true, true),
|
||||
("/safety", safety::routes(), true, true),
|
||||
(
|
||||
"/auth/mfa",
|
||||
rocket_authifier::routes::mfa::routes(),
|
||||
true,
|
||||
true,
|
||||
),
|
||||
("/onboard", onboard::routes(), true, true),
|
||||
("/policy", policy::routes(), true, true),
|
||||
("/push", push::routes(), true, true),
|
||||
("/sync", sync::routes(), true, true),
|
||||
(
|
||||
"/auth/account",
|
||||
rocket_authifier::routes::account::routes(),
|
||||
true,
|
||||
true,
|
||||
),
|
||||
(
|
||||
"/auth/session",
|
||||
rocket_authifier::routes::session::routes(),
|
||||
true,
|
||||
true,
|
||||
),
|
||||
(
|
||||
"/admin",
|
||||
admin::routes(),
|
||||
config.features.admin_api_enabled,
|
||||
config.features.admin_api_show_spec,
|
||||
),
|
||||
(
|
||||
"/webhooks",
|
||||
webhooks::routes(),
|
||||
config.features.webhooks_enabled,
|
||||
true,
|
||||
),
|
||||
];
|
||||
|
||||
for (base, (routes, openapi), enabled, show_spec) in routes {
|
||||
if !enabled {
|
||||
continue;
|
||||
}
|
||||
|
||||
rocket = rocket.mount(base, routes);
|
||||
if show_spec {
|
||||
openapi_list.push((base, openapi));
|
||||
}
|
||||
}
|
||||
|
||||
if config.features.webhooks_enabled {
|
||||
mount_endpoints_and_merged_docs! {
|
||||
rocket, "/0.8".to_owned(), settings,
|
||||
"/" => (vec![], custom_openapi_spec()),
|
||||
"" => openapi_get_routes_spec![root::root],
|
||||
"/users" => users::routes(),
|
||||
"/bots" => bots::routes(),
|
||||
"/channels" => channels::routes(),
|
||||
"/servers" => servers::routes(),
|
||||
"/invites" => invites::routes(),
|
||||
"/custom" => customisation::routes(),
|
||||
"/safety" => safety::routes(),
|
||||
"/auth/account" => rocket_authifier::routes::account::routes(),
|
||||
"/auth/session" => rocket_authifier::routes::session::routes(),
|
||||
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
|
||||
"/onboard" => onboard::routes(),
|
||||
"/push" => push::routes(),
|
||||
"/sync" => sync::routes(),
|
||||
"/webhooks" => webhooks::routes()
|
||||
let openapi_docs =
|
||||
match revolt_rocket_okapi::revolt_okapi::merge::marge_spec_list(&openapi_list) {
|
||||
Ok(docs) => docs,
|
||||
Err(err) => panic!("Could not merge OpenAPI spec: {}", err),
|
||||
};
|
||||
} else {
|
||||
mount_endpoints_and_merged_docs! {
|
||||
rocket, "/0.8".to_owned(), settings,
|
||||
"/" => (vec![], custom_openapi_spec()),
|
||||
"" => openapi_get_routes_spec![root::root],
|
||||
"/users" => users::routes(),
|
||||
"/bots" => bots::routes(),
|
||||
"/channels" => channels::routes(),
|
||||
"/servers" => servers::routes(),
|
||||
"/invites" => invites::routes(),
|
||||
"/custom" => customisation::routes(),
|
||||
"/safety" => safety::routes(),
|
||||
"/auth/account" => rocket_authifier::routes::account::routes(),
|
||||
"/auth/session" => rocket_authifier::routes::session::routes(),
|
||||
"/auth/mfa" => rocket_authifier::routes::mfa::routes(),
|
||||
"/onboard" => onboard::routes(),
|
||||
"/push" => push::routes(),
|
||||
"/sync" => sync::routes()
|
||||
};
|
||||
}
|
||||
|
||||
rocket = rocket.mount(
|
||||
"/",
|
||||
vec![revolt_rocket_okapi::get_openapi_route(
|
||||
openapi_docs,
|
||||
&settings,
|
||||
)],
|
||||
);
|
||||
|
||||
rocket
|
||||
}
|
||||
@@ -238,11 +229,6 @@ fn custom_openapi_spec() -> OpenApi {
|
||||
description: Some("Local Revolt Environment".to_owned()),
|
||||
..Default::default()
|
||||
},
|
||||
Server {
|
||||
url: "http://local.revolt.chat:14702/0.8".to_owned(),
|
||||
description: Some("Local Revolt Environment (v0.8)".to_owned()),
|
||||
..Default::default()
|
||||
},
|
||||
],
|
||||
external_docs: Some(ExternalDocs {
|
||||
url: "https://developers.revolt.chat".to_owned(),
|
||||
@@ -251,6 +237,13 @@ fn custom_openapi_spec() -> OpenApi {
|
||||
}),
|
||||
extensions,
|
||||
tags: vec![
|
||||
Tag {
|
||||
name: "Admin".to_owned(),
|
||||
description: Some(
|
||||
"Used when running your own instance to manage and moderate".to_owned(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
Tag {
|
||||
name: "Core".to_owned(),
|
||||
description: Some(
|
||||
|
||||
@@ -123,6 +123,7 @@ pub async fn report_content(
|
||||
additional_context: data.additional_context,
|
||||
status: ReportStatus::Created {},
|
||||
notes: String::new(),
|
||||
case_id: None,
|
||||
};
|
||||
|
||||
db.insert_report(&report).await?;
|
||||
|
||||
Reference in New Issue
Block a user