You've already forked AstralRinth
forked from didirus/AstralRinth
V2 removal and _internal rerouting (#770)
* deleteed v3 exclusive routes * moved routes around * fixed linkage that movement broke * initial merge errors * fixes
This commit is contained in:
@@ -1,14 +1,12 @@
|
|||||||
pub mod checks;
|
pub mod checks;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
pub mod flows;
|
|
||||||
pub mod oauth;
|
pub mod oauth;
|
||||||
pub mod pats;
|
pub mod templates;
|
||||||
pub mod session;
|
|
||||||
mod templates;
|
|
||||||
pub mod validate;
|
pub mod validate;
|
||||||
pub use checks::{
|
pub use checks::{
|
||||||
filter_authorized_projects, filter_authorized_versions, is_authorized, is_authorized_version,
|
filter_authorized_projects, filter_authorized_versions, is_authorized, is_authorized_version,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
// pub use pat::{generate_pat, PersonalAccessToken};
|
// pub use pat::{generate_pat, PersonalAccessToken};
|
||||||
pub use validate::{check_is_moderator_from_headers, get_user_from_headers};
|
pub use validate::{check_is_moderator_from_headers, get_user_from_headers};
|
||||||
|
|
||||||
@@ -98,3 +96,16 @@ impl AuthenticationError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy, Debug)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum AuthProvider {
|
||||||
|
#[default]
|
||||||
|
GitHub,
|
||||||
|
Discord,
|
||||||
|
Microsoft,
|
||||||
|
GitLab,
|
||||||
|
Google,
|
||||||
|
Steam,
|
||||||
|
PayPal,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::auth::flows::AuthProvider;
|
use super::AuthProvider;
|
||||||
use crate::auth::session::get_session_metadata;
|
|
||||||
use crate::auth::AuthenticationError;
|
use crate::auth::AuthenticationError;
|
||||||
use crate::database::models::user_item;
|
use crate::database::models::user_item;
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::models::pats::Scopes;
|
use crate::models::pats::Scopes;
|
||||||
use crate::models::users::{Role, User, UserId, UserPayoutData};
|
use crate::models::users::{Role, User, UserId, UserPayoutData};
|
||||||
use crate::queue::session::AuthQueue;
|
use crate::queue::session::AuthQueue;
|
||||||
|
use crate::routes::internal::session::get_session_metadata;
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use reqwest::header::{HeaderValue, AUTHORIZATION};
|
use reqwest::header::{HeaderValue, AUTHORIZATION};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use super::ids::*;
|
use super::ids::*;
|
||||||
use crate::auth::oauth::uris::OAuthRedirectUris;
|
use crate::auth::oauth::uris::OAuthRedirectUris;
|
||||||
|
use crate::auth::AuthProvider;
|
||||||
use crate::database::models::DatabaseError;
|
use crate::database::models::DatabaseError;
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::{auth::flows::AuthProvider, models::pats::Scopes};
|
use crate::models::pats::Scopes;
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|||||||
@@ -276,6 +276,7 @@ pub fn app_config(cfg: &mut web::ServiceConfig, labrinth_config: LabrinthConfig)
|
|||||||
.app_data(labrinth_config.active_sockets.clone())
|
.app_data(labrinth_config.active_sockets.clone())
|
||||||
.configure(routes::v2::config)
|
.configure(routes::v2::config)
|
||||||
.configure(routes::v3::config)
|
.configure(routes::v3::config)
|
||||||
|
.configure(routes::internal::config)
|
||||||
.configure(routes::root_config)
|
.configure(routes::root_config)
|
||||||
.default_service(web::get().wrap(default_cors()).to(routes::not_found));
|
.default_service(web::get().wrap(default_cors()).to(routes::not_found));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use super::ids::Base62Id;
|
use super::ids::Base62Id;
|
||||||
use crate::auth::flows::AuthProvider;
|
use crate::{auth::AuthProvider, bitflags_serde_impl};
|
||||||
use crate::bitflags_serde_impl;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::auth::session::SessionMetadata;
|
|
||||||
use crate::database::models::pat_item::PersonalAccessToken;
|
use crate::database::models::pat_item::PersonalAccessToken;
|
||||||
use crate::database::models::session_item::Session;
|
use crate::database::models::session_item::Session;
|
||||||
use crate::database::models::{DatabaseError, OAuthAccessTokenId, PatId, SessionId, UserId};
|
use crate::database::models::{DatabaseError, OAuthAccessTokenId, PatId, SessionId, UserId};
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
|
use crate::routes::internal::session::SessionMetadata;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::auth::email::send_email;
|
use crate::auth::email::send_email;
|
||||||
use crate::auth::session::issue_session;
|
|
||||||
use crate::auth::validate::get_user_record_from_bearer_token;
|
use crate::auth::validate::get_user_record_from_bearer_token;
|
||||||
use crate::auth::{get_user_from_headers, AuthenticationError};
|
use crate::auth::{get_user_from_headers, AuthProvider, AuthenticationError};
|
||||||
use crate::database::models::flow_item::Flow;
|
use crate::database::models::flow_item::Flow;
|
||||||
use crate::database::redis::RedisPool;
|
use crate::database::redis::RedisPool;
|
||||||
use crate::file_hosting::FileHost;
|
use crate::file_hosting::FileHost;
|
||||||
@@ -11,6 +10,7 @@ use crate::models::pats::Scopes;
|
|||||||
use crate::models::users::{Badges, Role};
|
use crate::models::users::{Badges, Role};
|
||||||
use crate::queue::session::AuthQueue;
|
use crate::queue::session::AuthQueue;
|
||||||
use crate::queue::socket::ActiveSockets;
|
use crate::queue::socket::ActiveSockets;
|
||||||
|
use crate::routes::internal::session::issue_session;
|
||||||
use crate::routes::ApiError;
|
use crate::routes::ApiError;
|
||||||
use crate::util::captcha::check_turnstile_captcha;
|
use crate::util::captcha::check_turnstile_captcha;
|
||||||
use crate::util::env::parse_strings_from_var;
|
use crate::util::env::parse_strings_from_var;
|
||||||
@@ -56,19 +56,6 @@ pub fn config(cfg: &mut ServiceConfig) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Eq, PartialEq, Clone, Copy, Debug)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum AuthProvider {
|
|
||||||
#[default]
|
|
||||||
GitHub,
|
|
||||||
Discord,
|
|
||||||
Microsoft,
|
|
||||||
GitLab,
|
|
||||||
Google,
|
|
||||||
Steam,
|
|
||||||
PayPal,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TempUser {
|
pub struct TempUser {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -1146,7 +1133,7 @@ pub async fn auth_callback(
|
|||||||
client: Data<PgPool>,
|
client: Data<PgPool>,
|
||||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||||
redis: Data<RedisPool>,
|
redis: Data<RedisPool>,
|
||||||
) -> Result<HttpResponse, super::templates::ErrorPage> {
|
) -> Result<HttpResponse, crate::auth::templates::ErrorPage> {
|
||||||
let state_string = query
|
let state_string = query
|
||||||
.get("state")
|
.get("state")
|
||||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?
|
.ok_or_else(|| AuthenticationError::InvalidCredentials)?
|
||||||
@@ -1263,7 +1250,7 @@ pub async fn auth_callback(
|
|||||||
|
|
||||||
let _ = ws_conn.close(None).await;
|
let _ = ws_conn.close(None).await;
|
||||||
|
|
||||||
return Ok(super::templates::Success {
|
return Ok(crate::auth::templates::Success {
|
||||||
icon: user.avatar_url.as_deref().unwrap_or("https://cdn-raw.modrinth.com/placeholder.svg"),
|
icon: user.avatar_url.as_deref().unwrap_or("https://cdn-raw.modrinth.com/placeholder.svg"),
|
||||||
name: &user.username,
|
name: &user.username,
|
||||||
}.render());
|
}.render());
|
||||||
@@ -1322,7 +1309,7 @@ pub async fn auth_callback(
|
|||||||
.await.map_err(|_| AuthenticationError::SocketError)?;
|
.await.map_err(|_| AuthenticationError::SocketError)?;
|
||||||
let _ = ws_conn.close(None).await;
|
let _ = ws_conn.close(None).await;
|
||||||
|
|
||||||
return Ok(super::templates::Success {
|
return Ok(crate::auth::templates::Success {
|
||||||
icon: user.avatar_url.as_deref().unwrap_or("https://cdn-raw.modrinth.com/placeholder.svg"),
|
icon: user.avatar_url.as_deref().unwrap_or("https://cdn-raw.modrinth.com/placeholder.svg"),
|
||||||
name: &user.username,
|
name: &user.username,
|
||||||
}.render());
|
}.render());
|
||||||
@@ -2356,7 +2343,7 @@ fn send_email_verify(
|
|||||||
email: String,
|
email: String,
|
||||||
flow: String,
|
flow: String,
|
||||||
opener: &str,
|
opener: &str,
|
||||||
) -> Result<(), super::email::MailError> {
|
) -> Result<(), crate::auth::email::MailError> {
|
||||||
send_email(
|
send_email(
|
||||||
email,
|
email,
|
||||||
"Verify your email",
|
"Verify your email",
|
||||||
21
src/routes/internal/mod.rs
Normal file
21
src/routes/internal/mod.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
mod admin;
|
||||||
|
pub mod flows;
|
||||||
|
pub mod pats;
|
||||||
|
pub mod session;
|
||||||
|
|
||||||
|
use super::v3::oauth_clients;
|
||||||
|
pub use super::ApiError;
|
||||||
|
use crate::util::cors::default_cors;
|
||||||
|
|
||||||
|
pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||||
|
cfg.service(
|
||||||
|
actix_web::web::scope("_internal")
|
||||||
|
.wrap(default_cors())
|
||||||
|
.configure(admin::config)
|
||||||
|
// TODO: write tests that catch these
|
||||||
|
.configure(oauth_clients::config)
|
||||||
|
.configure(session::config)
|
||||||
|
.configure(flows::config)
|
||||||
|
.configure(pats::config),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use actix_web::http::StatusCode;
|
|||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
|
pub mod internal;
|
||||||
pub mod v2;
|
pub mod v2;
|
||||||
pub mod v3;
|
pub mod v3;
|
||||||
|
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
use super::ApiError;
|
|
||||||
use crate::database::redis::RedisPool;
|
|
||||||
use crate::routes::v3;
|
|
||||||
use crate::{models::ids::VersionId, queue::session::AuthQueue};
|
|
||||||
use actix_web::{get, web, HttpRequest, HttpResponse};
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::PgPool;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(
|
|
||||||
web::scope("analytics")
|
|
||||||
.service(playtimes_get)
|
|
||||||
.service(views_get)
|
|
||||||
.service(downloads_get)
|
|
||||||
.service(revenue_get)
|
|
||||||
.service(countries_downloads_get)
|
|
||||||
.service(countries_views_get),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The json data to be passed to fetch analytic data
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
/// start_date and end_date are optional, and default to two weeks ago, and the maximum date respectively
|
|
||||||
/// start_date and end_date are inclusive
|
|
||||||
/// resolution_minutes is optional. This refers to the window by which we are looking (every day, every minute, etc) and defaults to 1440 (1 day)
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
pub struct GetData {
|
|
||||||
// only one of project_ids or version_ids should be used
|
|
||||||
// if neither are provided, all projects the user has access to will be used
|
|
||||||
pub project_ids: Option<String>,
|
|
||||||
pub version_ids: Option<String>,
|
|
||||||
|
|
||||||
pub start_date: Option<DateTime<Utc>>, // defaults to 2 weeks ago
|
|
||||||
pub end_date: Option<DateTime<Utc>>, // defaults to now
|
|
||||||
|
|
||||||
pub resolution_minutes: Option<u32>, // defaults to 1 day. Ignored in routes that do not aggregate over a resolution (eg: /countries)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get playtime data for a set of projects or versions
|
|
||||||
/// Data is returned as a hashmap of project/version ids to a hashmap of days to playtime data
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "20230824": 23
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
pub struct FetchedPlaytime {
|
|
||||||
pub time: u64,
|
|
||||||
pub total_seconds: u64,
|
|
||||||
pub loader_seconds: HashMap<String, u64>,
|
|
||||||
pub game_version_seconds: HashMap<String, u64>,
|
|
||||||
pub parent_seconds: HashMap<VersionId, u64>,
|
|
||||||
}
|
|
||||||
#[get("playtime")]
|
|
||||||
pub async fn playtimes_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
clickhouse: web::Data<clickhouse::Client>,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::playtimes_get(
|
|
||||||
req,
|
|
||||||
clickhouse,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: data.version_ids,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get view data for a set of projects or versions
|
|
||||||
/// Data is returned as a hashmap of project/version ids to a hashmap of days to views
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "20230824": 1090
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
#[get("views")]
|
|
||||||
pub async fn views_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
clickhouse: web::Data<clickhouse::Client>,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::views_get(
|
|
||||||
req,
|
|
||||||
clickhouse,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: data.version_ids,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get download data for a set of projects or versions
|
|
||||||
/// Data is returned as a hashmap of project/version ids to a hashmap of days to downloads
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "20230824": 32
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
#[get("downloads")]
|
|
||||||
pub async fn downloads_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
clickhouse: web::Data<clickhouse::Client>,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::downloads_get(
|
|
||||||
req,
|
|
||||||
clickhouse,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: data.version_ids,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get payout data for a set of projects
|
|
||||||
/// Data is returned as a hashmap of project ids to a hashmap of days to amount earned per day
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "20230824": 0.001
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// ONLY project IDs can be used. Unauthorized projects will be filtered out.
|
|
||||||
#[get("revenue")]
|
|
||||||
pub async fn revenue_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::revenue_get(
|
|
||||||
req,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: None,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get country data for a set of projects or versions
|
|
||||||
/// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to downloads.
|
|
||||||
/// Unknown countries are labeled "".
|
|
||||||
/// This is usuable to see significant performing countries per project
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "CAN": 22
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
/// For this endpoint, provided dates are a range to aggregate over, not specific days to fetch
|
|
||||||
#[get("countries/downloads")]
|
|
||||||
pub async fn countries_downloads_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
clickhouse: web::Data<clickhouse::Client>,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::countries_downloads_get(
|
|
||||||
req,
|
|
||||||
clickhouse,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: data.version_ids,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get country data for a set of projects or versions
|
|
||||||
/// Data is returned as a hashmap of project/version ids to a hashmap of coutnry to views.
|
|
||||||
/// Unknown countries are labeled "".
|
|
||||||
/// This is usuable to see significant performing countries per project
|
|
||||||
/// eg:
|
|
||||||
/// {
|
|
||||||
/// "4N1tEhnO": {
|
|
||||||
/// "CAN": 56165
|
|
||||||
/// }
|
|
||||||
///}
|
|
||||||
/// Either a list of project_ids or version_ids can be used, but not both. Unauthorized projects/versions will be filtered out.
|
|
||||||
/// For this endpoint, provided dates are a range to aggregate over, not specific days to fetch
|
|
||||||
#[get("countries/views")]
|
|
||||||
pub async fn countries_views_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
clickhouse: web::Data<clickhouse::Client>,
|
|
||||||
data: web::Query<GetData>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let data = data.into_inner();
|
|
||||||
v3::analytics_get::countries_views_get(
|
|
||||||
req,
|
|
||||||
clickhouse,
|
|
||||||
web::Query(v3::analytics_get::GetData {
|
|
||||||
project_ids: data.project_ids,
|
|
||||||
version_ids: data.version_ids,
|
|
||||||
start_date: data.start_date,
|
|
||||||
end_date: data.end_date,
|
|
||||||
resolution_minutes: data.resolution_minutes,
|
|
||||||
}),
|
|
||||||
session_queue,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
use crate::database::redis::RedisPool;
|
|
||||||
use crate::file_hosting::FileHost;
|
|
||||||
use crate::models::collections::CollectionStatus;
|
|
||||||
use crate::queue::session::AuthQueue;
|
|
||||||
use crate::routes::v3::project_creation::CreateError;
|
|
||||||
use crate::routes::{v3, ApiError};
|
|
||||||
use actix_web::web::Data;
|
|
||||||
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::PgPool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use validator::Validate;
|
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(collections_get);
|
|
||||||
cfg.service(collection_create);
|
|
||||||
cfg.service(
|
|
||||||
web::scope("collection")
|
|
||||||
.service(collection_get)
|
|
||||||
.service(collection_delete)
|
|
||||||
.service(collection_edit)
|
|
||||||
.service(collection_icon_edit)
|
|
||||||
.service(delete_collection_icon),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Validate, Clone)]
|
|
||||||
pub struct CollectionCreateData {
|
|
||||||
#[validate(
|
|
||||||
length(min = 3, max = 64),
|
|
||||||
custom(function = "crate::util::validate::validate_name")
|
|
||||||
)]
|
|
||||||
/// The title or name of the project.
|
|
||||||
pub title: String,
|
|
||||||
#[validate(length(min = 3, max = 255))]
|
|
||||||
/// A short description of the collection.
|
|
||||||
pub description: String,
|
|
||||||
#[validate(length(max = 32))]
|
|
||||||
#[serde(default = "Vec::new")]
|
|
||||||
/// A list of initial projects to use with the created collection
|
|
||||||
pub projects: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("collection")]
|
|
||||||
pub async fn collection_create(
|
|
||||||
req: HttpRequest,
|
|
||||||
collection_create_data: web::Json<CollectionCreateData>,
|
|
||||||
client: Data<PgPool>,
|
|
||||||
redis: Data<RedisPool>,
|
|
||||||
session_queue: Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, CreateError> {
|
|
||||||
let collection_create_data = collection_create_data.into_inner();
|
|
||||||
v3::collections::collection_create(
|
|
||||||
req,
|
|
||||||
web::Json(v3::collections::CollectionCreateData {
|
|
||||||
title: collection_create_data.title,
|
|
||||||
description: collection_create_data.description,
|
|
||||||
projects: collection_create_data.projects,
|
|
||||||
}),
|
|
||||||
client,
|
|
||||||
redis,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CollectionIds {
|
|
||||||
pub ids: String,
|
|
||||||
}
|
|
||||||
#[get("collections")]
|
|
||||||
pub async fn collections_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
web::Query(ids): web::Query<CollectionIds>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::collections::collections_get(
|
|
||||||
req,
|
|
||||||
web::Query(v3::collections::CollectionIds { ids: ids.ids }),
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("{id}")]
|
|
||||||
pub async fn collection_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::collections::collection_get(req, info, pool, redis, session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Validate)]
|
|
||||||
pub struct EditCollection {
|
|
||||||
#[validate(
|
|
||||||
length(min = 3, max = 64),
|
|
||||||
custom(function = "crate::util::validate::validate_name")
|
|
||||||
)]
|
|
||||||
pub title: Option<String>,
|
|
||||||
#[validate(length(min = 3, max = 256))]
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub status: Option<CollectionStatus>,
|
|
||||||
#[validate(length(max = 64))]
|
|
||||||
pub new_projects: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("{id}")]
|
|
||||||
pub async fn collection_edit(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
new_collection: web::Json<EditCollection>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let new_collection = new_collection.into_inner();
|
|
||||||
v3::collections::collection_edit(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool,
|
|
||||||
web::Json(v3::collections::EditCollection {
|
|
||||||
title: new_collection.title,
|
|
||||||
description: new_collection.description,
|
|
||||||
status: new_collection.status,
|
|
||||||
new_projects: new_collection.new_projects,
|
|
||||||
}),
|
|
||||||
redis,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Extension {
|
|
||||||
pub ext: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("{id}/icon")]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub async fn collection_icon_edit(
|
|
||||||
web::Query(ext): web::Query<Extension>,
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
|
||||||
payload: web::Payload,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::collections::collection_icon_edit(
|
|
||||||
web::Query(v3::collections::Extension { ext: ext.ext }),
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
file_host,
|
|
||||||
payload,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("{id}/icon")]
|
|
||||||
pub async fn delete_collection_icon(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::collections::delete_collection_icon(req, info, pool, redis, file_host, session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("{id}")]
|
|
||||||
pub async fn collection_delete(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::collections::collection_delete(req, info, pool, redis, session_queue).await
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::database::redis::RedisPool;
|
|
||||||
use crate::file_hosting::FileHost;
|
|
||||||
use crate::models::ids::{ThreadMessageId, VersionId};
|
|
||||||
use crate::models::reports::ReportId;
|
|
||||||
use crate::queue::session::AuthQueue;
|
|
||||||
use crate::routes::{v3, ApiError};
|
|
||||||
use actix_web::{post, web, HttpRequest, HttpResponse};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::PgPool;
|
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(images_add);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ImageUpload {
|
|
||||||
pub ext: String,
|
|
||||||
|
|
||||||
// Context must be an allowed context
|
|
||||||
// currently: project, version, thread_message, report
|
|
||||||
pub context: String,
|
|
||||||
|
|
||||||
// Optional context id to associate with
|
|
||||||
pub project_id: Option<String>, // allow slug or id
|
|
||||||
pub version_id: Option<VersionId>,
|
|
||||||
pub thread_message_id: Option<ThreadMessageId>,
|
|
||||||
pub report_id: Option<ReportId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("image")]
|
|
||||||
pub async fn images_add(
|
|
||||||
req: HttpRequest,
|
|
||||||
web::Query(data): web::Query<ImageUpload>,
|
|
||||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
|
||||||
payload: web::Payload,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::images::images_add(
|
|
||||||
req,
|
|
||||||
web::Query(v3::images::ImageUpload {
|
|
||||||
ext: data.ext,
|
|
||||||
context: data.context,
|
|
||||||
project_id: data.project_id,
|
|
||||||
version_id: data.version_id,
|
|
||||||
thread_message_id: data.thread_message_id,
|
|
||||||
report_id: data.report_id,
|
|
||||||
}),
|
|
||||||
file_host,
|
|
||||||
payload,
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
mod admin;
|
mod admin;
|
||||||
mod analytics_get;
|
|
||||||
mod collections;
|
|
||||||
mod images;
|
|
||||||
mod moderation;
|
mod moderation;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
mod organizations;
|
|
||||||
pub(crate) mod project_creation;
|
pub(crate) mod project_creation;
|
||||||
mod projects;
|
mod projects;
|
||||||
mod reports;
|
mod reports;
|
||||||
@@ -25,17 +21,13 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
|||||||
actix_web::web::scope("v2")
|
actix_web::web::scope("v2")
|
||||||
.wrap(default_cors())
|
.wrap(default_cors())
|
||||||
.configure(admin::config)
|
.configure(admin::config)
|
||||||
.configure(analytics_get::config)
|
|
||||||
// Todo: separate these- they need to also follow v2-v3 conversion
|
// Todo: separate these- they need to also follow v2-v3 conversion
|
||||||
.configure(crate::auth::session::config)
|
.configure(super::internal::session::config)
|
||||||
.configure(crate::auth::flows::config)
|
.configure(super::internal::flows::config)
|
||||||
.configure(crate::auth::pats::config)
|
.configure(super::internal::pats::config)
|
||||||
.configure(moderation::config)
|
.configure(moderation::config)
|
||||||
.configure(notifications::config)
|
.configure(notifications::config)
|
||||||
.configure(organizations::config)
|
|
||||||
.configure(project_creation::config)
|
.configure(project_creation::config)
|
||||||
.configure(collections::config)
|
|
||||||
.configure(images::config)
|
|
||||||
.configure(projects::config)
|
.configure(projects::config)
|
||||||
.configure(reports::config)
|
.configure(reports::config)
|
||||||
.configure(statistics::config)
|
.configure(statistics::config)
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
use crate::database::redis::RedisPool;
|
|
||||||
use crate::file_hosting::FileHost;
|
|
||||||
use crate::models::projects::Project;
|
|
||||||
use crate::models::v2::projects::LegacyProject;
|
|
||||||
use crate::queue::session::AuthQueue;
|
|
||||||
use crate::routes::v3::project_creation::CreateError;
|
|
||||||
use crate::routes::{v2_reroute, v3, ApiError};
|
|
||||||
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::PgPool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use validator::Validate;
|
|
||||||
|
|
||||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
|
||||||
cfg.service(organizations_get).service(organization_create);
|
|
||||||
cfg.service(
|
|
||||||
web::scope("organization")
|
|
||||||
.service(organization_get)
|
|
||||||
.service(organizations_edit)
|
|
||||||
.service(organization_delete)
|
|
||||||
.service(organization_projects_get)
|
|
||||||
.service(organization_projects_add)
|
|
||||||
.service(organization_projects_remove)
|
|
||||||
.service(organization_icon_edit)
|
|
||||||
.service(delete_organization_icon)
|
|
||||||
.service(super::teams::team_members_get_organization),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Validate)]
|
|
||||||
pub struct NewOrganization {
|
|
||||||
#[validate(
|
|
||||||
length(min = 3, max = 64),
|
|
||||||
regex = "crate::util::validate::RE_URL_SAFE"
|
|
||||||
)]
|
|
||||||
// Title of the organization, also used as slug
|
|
||||||
pub title: String,
|
|
||||||
#[validate(length(min = 3, max = 256))]
|
|
||||||
pub description: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("organization")]
|
|
||||||
pub async fn organization_create(
|
|
||||||
req: HttpRequest,
|
|
||||||
new_organization: web::Json<NewOrganization>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, CreateError> {
|
|
||||||
let new_organization = new_organization.into_inner();
|
|
||||||
v3::organizations::organization_create(
|
|
||||||
req,
|
|
||||||
web::Json(v3::organizations::NewOrganization {
|
|
||||||
title: new_organization.title,
|
|
||||||
description: new_organization.description,
|
|
||||||
}),
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("{id}")]
|
|
||||||
pub async fn organization_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::organization_get(req, info, pool.clone(), redis.clone(), session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct OrganizationIds {
|
|
||||||
pub ids: String,
|
|
||||||
}
|
|
||||||
#[get("organizations")]
|
|
||||||
pub async fn organizations_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
web::Query(ids): web::Query<OrganizationIds>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::organizations_get(
|
|
||||||
req,
|
|
||||||
web::Query(v3::organizations::OrganizationIds { ids: ids.ids }),
|
|
||||||
pool,
|
|
||||||
redis,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Validate)]
|
|
||||||
pub struct OrganizationEdit {
|
|
||||||
#[validate(length(min = 3, max = 256))]
|
|
||||||
pub description: Option<String>,
|
|
||||||
#[validate(
|
|
||||||
length(min = 3, max = 64),
|
|
||||||
regex = "crate::util::validate::RE_URL_SAFE"
|
|
||||||
)]
|
|
||||||
// Title of the organization, also used as slug
|
|
||||||
pub title: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("{id}")]
|
|
||||||
pub async fn organizations_edit(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
new_organization: web::Json<OrganizationEdit>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let new_organization = new_organization.into_inner();
|
|
||||||
v3::organizations::organizations_edit(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
web::Json(v3::organizations::OrganizationEdit {
|
|
||||||
description: new_organization.description,
|
|
||||||
title: new_organization.title,
|
|
||||||
}),
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("{id}")]
|
|
||||||
pub async fn organization_delete(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::organization_delete(req, info, pool.clone(), redis.clone(), session_queue)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("{id}/projects")]
|
|
||||||
pub async fn organization_projects_get(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let response = v3::organizations::organization_projects_get(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Convert v3 projects to v2
|
|
||||||
match v2_reroute::extract_ok_json::<Vec<Project>>(response).await {
|
|
||||||
Ok(project) => {
|
|
||||||
let legacy_projects = LegacyProject::from_many(project, &**pool, &redis).await?;
|
|
||||||
Ok(HttpResponse::Ok().json(legacy_projects))
|
|
||||||
}
|
|
||||||
Err(response) => Ok(response),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct OrganizationProjectAdd {
|
|
||||||
pub project_id: String, // Also allow title/slug
|
|
||||||
}
|
|
||||||
#[post("{id}/projects")]
|
|
||||||
pub async fn organization_projects_add(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
project_info: web::Json<OrganizationProjectAdd>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
let project_info = project_info.into_inner();
|
|
||||||
v3::organizations::organization_projects_add(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
web::Json(v3::organizations::OrganizationProjectAdd {
|
|
||||||
project_id: project_info.project_id,
|
|
||||||
}),
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("{organization_id}/projects/{project_id}")]
|
|
||||||
pub async fn organization_projects_remove(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String, String)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::organization_projects_remove(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Extension {
|
|
||||||
pub ext: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("{id}/icon")]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub async fn organization_icon_edit(
|
|
||||||
web::Query(ext): web::Query<Extension>,
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
|
||||||
payload: web::Payload,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::organization_icon_edit(
|
|
||||||
web::Query(v3::organizations::Extension { ext: ext.ext }),
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
file_host,
|
|
||||||
payload,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("{id}/icon")]
|
|
||||||
pub async fn delete_organization_icon(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::organizations::delete_organization_icon(
|
|
||||||
req,
|
|
||||||
info,
|
|
||||||
pool.clone(),
|
|
||||||
redis.clone(),
|
|
||||||
file_host,
|
|
||||||
session_queue,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
@@ -37,17 +37,6 @@ pub async fn team_members_get_project(
|
|||||||
v3::teams::team_members_get_project(req, info, pool, redis, session_queue).await
|
v3::teams::team_members_get_project(req, info, pool, redis, session_queue).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("{id}/members")]
|
|
||||||
pub async fn team_members_get_organization(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::teams::team_members_get_organization(req, info, pool, redis, session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns all members of a team, but not necessarily those of a project-team's organization (unlike team_members_get_project)
|
// Returns all members of a team, but not necessarily those of a project-team's organization (unlike team_members_get_project)
|
||||||
#[get("{id}/members")]
|
#[get("{id}/members")]
|
||||||
pub async fn team_members_get(
|
pub async fn team_members_get(
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||||||
cfg.service(
|
cfg.service(
|
||||||
web::scope("user")
|
web::scope("user")
|
||||||
.service(user_get)
|
.service(user_get)
|
||||||
.service(orgs_list)
|
|
||||||
.service(projects_list)
|
.service(projects_list)
|
||||||
.service(collections_list)
|
|
||||||
.service(user_delete)
|
.service(user_delete)
|
||||||
.service(user_edit)
|
.service(user_edit)
|
||||||
.service(user_icon_edit)
|
.service(user_icon_edit)
|
||||||
@@ -85,28 +83,6 @@ pub async fn projects_list(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("{user_id}/collections")]
|
|
||||||
pub async fn collections_list(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::users::collections_list(req, info, pool, redis, session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("{user_id}/organizations")]
|
|
||||||
pub async fn orgs_list(
|
|
||||||
req: HttpRequest,
|
|
||||||
info: web::Path<(String,)>,
|
|
||||||
pool: web::Data<PgPool>,
|
|
||||||
redis: web::Data<RedisPool>,
|
|
||||||
session_queue: web::Data<AuthQueue>,
|
|
||||||
) -> Result<HttpResponse, ApiError> {
|
|
||||||
v3::users::orgs_list(req, info, pool, redis, session_queue).await
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
|
static ref RE_URL_SAFE: Regex = Regex::new(r"^[a-zA-Z0-9_-]*$").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use crate::util::cors::default_cors;
|
|||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
pub mod admin;
|
|
||||||
pub mod analytics_get;
|
pub mod analytics_get;
|
||||||
pub mod collections;
|
pub mod collections;
|
||||||
pub mod images;
|
pub mod images;
|
||||||
@@ -29,13 +28,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||||||
cfg.service(
|
cfg.service(
|
||||||
web::scope("v3")
|
web::scope("v3")
|
||||||
.wrap(default_cors())
|
.wrap(default_cors())
|
||||||
.configure(admin::config)
|
|
||||||
.configure(analytics_get::config)
|
.configure(analytics_get::config)
|
||||||
// TODO: write tests that catch these
|
|
||||||
.configure(oauth_clients::config)
|
|
||||||
.configure(crate::auth::session::config)
|
|
||||||
.configure(crate::auth::flows::config)
|
|
||||||
.configure(crate::auth::pats::config)
|
|
||||||
.configure(collections::config)
|
.configure(collections::config)
|
||||||
.configure(images::config)
|
.configure(images::config)
|
||||||
.configure(moderation::config)
|
.configure(moderation::config)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl Api for ApiV3 {
|
|||||||
|
|
||||||
async fn reset_search_index(&self) -> ServiceResponse {
|
async fn reset_search_index(&self) -> ServiceResponse {
|
||||||
let req = actix_web::test::TestRequest::post()
|
let req = actix_web::test::TestRequest::post()
|
||||||
.uri("/v3/admin/_force_reindex")
|
.uri("/_internal/admin/_force_reindex")
|
||||||
.append_header((
|
.append_header((
|
||||||
"Modrinth-Admin",
|
"Modrinth-Admin",
|
||||||
dotenvy::var("LABRINTH_ADMIN_KEY").unwrap(),
|
dotenvy::var("LABRINTH_ADMIN_KEY").unwrap(),
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ impl ApiV3 {
|
|||||||
pub async fn oauth_accept(&self, flow: &str, pat: &str) -> ServiceResponse {
|
pub async fn oauth_accept(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||||
self.call(
|
self.call(
|
||||||
TestRequest::post()
|
TestRequest::post()
|
||||||
.uri("/v3/oauth/accept")
|
.uri("/_internal/oauth/accept")
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.set_json(RespondToOAuthClientScopes {
|
.set_json(RespondToOAuthClientScopes {
|
||||||
flow: flow.to_string(),
|
flow: flow.to_string(),
|
||||||
@@ -68,7 +68,7 @@ impl ApiV3 {
|
|||||||
pub async fn oauth_reject(&self, flow: &str, pat: &str) -> ServiceResponse {
|
pub async fn oauth_reject(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||||
self.call(
|
self.call(
|
||||||
TestRequest::post()
|
TestRequest::post()
|
||||||
.uri("/v3/oauth/reject")
|
.uri("/_internal/oauth/reject")
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.set_json(RespondToOAuthClientScopes {
|
.set_json(RespondToOAuthClientScopes {
|
||||||
flow: flow.to_string(),
|
flow: flow.to_string(),
|
||||||
@@ -87,7 +87,7 @@ impl ApiV3 {
|
|||||||
) -> ServiceResponse {
|
) -> ServiceResponse {
|
||||||
self.call(
|
self.call(
|
||||||
TestRequest::post()
|
TestRequest::post()
|
||||||
.uri("/v3/oauth/token")
|
.uri("/_internal/oauth/token")
|
||||||
.append_header((AUTHORIZATION, client_secret))
|
.append_header((AUTHORIZATION, client_secret))
|
||||||
.set_form(TokenRequest {
|
.set_form(TokenRequest {
|
||||||
grant_type: "authorization_code".to_string(),
|
grant_type: "authorization_code".to_string(),
|
||||||
@@ -108,7 +108,7 @@ pub fn generate_authorize_uri(
|
|||||||
state: Option<&str>,
|
state: Option<&str>,
|
||||||
) -> String {
|
) -> String {
|
||||||
format!(
|
format!(
|
||||||
"/v3/oauth/authorize?client_id={}{}{}{}",
|
"/_internal/oauth/authorize?client_id={}{}{}{}",
|
||||||
urlencoding::encode(client_id),
|
urlencoding::encode(client_id),
|
||||||
optional_query_param("redirect_uri", redirect_uri),
|
optional_query_param("redirect_uri", redirect_uri),
|
||||||
optional_query_param("scope", scope),
|
optional_query_param("scope", scope),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl ApiV3 {
|
|||||||
) -> ServiceResponse {
|
) -> ServiceResponse {
|
||||||
let max_scopes = max_scopes.bits();
|
let max_scopes = max_scopes.bits();
|
||||||
let req = TestRequest::post()
|
let req = TestRequest::post()
|
||||||
.uri("/v3/oauth/app")
|
.uri("/_internal/oauth/app")
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -52,7 +52,7 @@ impl ApiV3 {
|
|||||||
|
|
||||||
pub async fn get_oauth_client(&self, client_id: String, pat: &str) -> ServiceResponse {
|
pub async fn get_oauth_client(&self, client_id: String, pat: &str) -> ServiceResponse {
|
||||||
let req = TestRequest::get()
|
let req = TestRequest::get()
|
||||||
.uri(&format!("/v3/oauth/app/{}", client_id))
|
.uri(&format!("/_internal/oauth/app/{}", client_id))
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
@@ -66,7 +66,10 @@ impl ApiV3 {
|
|||||||
pat: &str,
|
pat: &str,
|
||||||
) -> ServiceResponse {
|
) -> ServiceResponse {
|
||||||
let req = TestRequest::patch()
|
let req = TestRequest::patch()
|
||||||
.uri(&format!("/v3/oauth/app/{}", urlencoding::encode(client_id)))
|
.uri(&format!(
|
||||||
|
"/_internal/oauth/app/{}",
|
||||||
|
urlencoding::encode(client_id)
|
||||||
|
))
|
||||||
.set_json(edit)
|
.set_json(edit)
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.to_request();
|
.to_request();
|
||||||
@@ -76,7 +79,7 @@ impl ApiV3 {
|
|||||||
|
|
||||||
pub async fn delete_oauth_client(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
pub async fn delete_oauth_client(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
||||||
let req = TestRequest::delete()
|
let req = TestRequest::delete()
|
||||||
.uri(&format!("/v3/oauth/app/{}", client_id))
|
.uri(&format!("/_internal/oauth/app/{}", client_id))
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.to_request();
|
.to_request();
|
||||||
|
|
||||||
@@ -86,7 +89,7 @@ impl ApiV3 {
|
|||||||
pub async fn revoke_oauth_authorization(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
pub async fn revoke_oauth_authorization(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
||||||
let req = TestRequest::delete()
|
let req = TestRequest::delete()
|
||||||
.uri(&format!(
|
.uri(&format!(
|
||||||
"/v3/oauth/authorizations?client_id={}",
|
"/_internal/oauth/authorizations?client_id={}",
|
||||||
urlencoding::encode(client_id)
|
urlencoding::encode(client_id)
|
||||||
))
|
))
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
@@ -96,7 +99,7 @@ impl ApiV3 {
|
|||||||
|
|
||||||
pub async fn get_user_oauth_authorizations(&self, pat: &str) -> Vec<OAuthClientAuthorization> {
|
pub async fn get_user_oauth_authorizations(&self, pat: &str) -> Vec<OAuthClientAuthorization> {
|
||||||
let req = TestRequest::get()
|
let req = TestRequest::get()
|
||||||
.uri("/v3/oauth/authorizations")
|
.uri("/_internal/oauth/authorizations")
|
||||||
.append_header((AUTHORIZATION, pat))
|
.append_header((AUTHORIZATION, pat))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = self.call(req).await;
|
let resp = self.call(req).await;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub async fn pat_full_test() {
|
|||||||
with_test_environment_all(None, |test_env| async move {
|
with_test_environment_all(None, |test_env| async move {
|
||||||
// Create a PAT for a full test
|
// Create a PAT for a full test
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||||
@@ -43,7 +43,7 @@ pub async fn pat_full_test() {
|
|||||||
// Get PAT again
|
// Get PAT again
|
||||||
let req = test::TestRequest::get()
|
let req = test::TestRequest::get()
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = test_env.call(req).await;
|
let resp = test_env.call(req).await;
|
||||||
assert_eq!(resp.status().as_u16(), 200);
|
assert_eq!(resp.status().as_u16(), 200);
|
||||||
@@ -75,7 +75,7 @@ pub async fn pat_full_test() {
|
|||||||
|
|
||||||
// Change scopes and test again
|
// Change scopes and test again
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": 0,
|
"scopes": 0,
|
||||||
@@ -87,7 +87,7 @@ pub async fn pat_full_test() {
|
|||||||
|
|
||||||
// Change scopes back, and set expiry to the past, and test again
|
// Change scopes back, and set expiry to the past, and test again
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": Scopes::COLLECTION_CREATE,
|
"scopes": Scopes::COLLECTION_CREATE,
|
||||||
@@ -103,7 +103,7 @@ pub async fn pat_full_test() {
|
|||||||
|
|
||||||
// Change everything back to normal and test again
|
// Change everything back to normal and test again
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"expires": Utc::now() + Duration::days(1), // no longer expired!
|
"expires": Utc::now() + Duration::days(1), // no longer expired!
|
||||||
@@ -115,7 +115,7 @@ pub async fn pat_full_test() {
|
|||||||
|
|
||||||
// Patching to a bad expiry should fail
|
// Patching to a bad expiry should fail
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"expires": Utc::now() - Duration::days(1), // Past
|
"expires": Utc::now() - Duration::days(1), // Past
|
||||||
@@ -132,7 +132,7 @@ pub async fn pat_full_test() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": scope.bits(),
|
"scopes": scope.bits(),
|
||||||
@@ -148,7 +148,7 @@ pub async fn pat_full_test() {
|
|||||||
// Delete PAT
|
// Delete PAT
|
||||||
let req = test::TestRequest::delete()
|
let req = test::TestRequest::delete()
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.to_request();
|
.to_request();
|
||||||
let resp = test_env.call(req).await;
|
let resp = test_env.call(req).await;
|
||||||
assert_eq!(resp.status().as_u16(), 204);
|
assert_eq!(resp.status().as_u16(), 204);
|
||||||
@@ -162,7 +162,7 @@ pub async fn bad_pats() {
|
|||||||
with_test_environment_all(None, |test_env| async move {
|
with_test_environment_all(None, |test_env| async move {
|
||||||
// Creating a PAT with no name should fail
|
// Creating a PAT with no name should fail
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||||
@@ -175,7 +175,7 @@ pub async fn bad_pats() {
|
|||||||
// Name too short or too long should fail
|
// Name too short or too long should fail
|
||||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -189,7 +189,7 @@ pub async fn bad_pats() {
|
|||||||
|
|
||||||
// Creating a PAT with an expiry in the past should fail
|
// Creating a PAT with an expiry in the past should fail
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||||
@@ -207,7 +207,7 @@ pub async fn bad_pats() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": scope.bits(),
|
"scopes": scope.bits(),
|
||||||
@@ -224,7 +224,7 @@ pub async fn bad_pats() {
|
|||||||
|
|
||||||
// Create a 'good' PAT for patching
|
// Create a 'good' PAT for patching
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": Scopes::COLLECTION_CREATE,
|
"scopes": Scopes::COLLECTION_CREATE,
|
||||||
@@ -240,7 +240,7 @@ pub async fn bad_pats() {
|
|||||||
// Patching to a bad name should fail
|
// Patching to a bad name should fail
|
||||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
.uri("/v3/pat")
|
.uri("/_internal/pat")
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"name": name,
|
"name": name,
|
||||||
@@ -252,7 +252,7 @@ pub async fn bad_pats() {
|
|||||||
|
|
||||||
// Patching to a bad expiry should fail
|
// Patching to a bad expiry should fail
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"expires": Utc::now() - Duration::days(1), // Past
|
"expires": Utc::now() - Duration::days(1), // Past
|
||||||
@@ -269,7 +269,7 @@ pub async fn bad_pats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let req = test::TestRequest::patch()
|
let req = test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{}", id))
|
.uri(&format!("/_internal/pat/{}", id))
|
||||||
.append_header(("Authorization", USER_USER_PAT))
|
.append_header(("Authorization", USER_USER_PAT))
|
||||||
.set_json(json!({
|
.set_json(json!({
|
||||||
"scopes": scope.bits(),
|
"scopes": scope.bits(),
|
||||||
|
|||||||
@@ -1017,11 +1017,13 @@ pub async fn pat_scopes() {
|
|||||||
// Pat create
|
// Pat create
|
||||||
let pat_create = Scopes::PAT_CREATE;
|
let pat_create = Scopes::PAT_CREATE;
|
||||||
let req_gen = || {
|
let req_gen = || {
|
||||||
test::TestRequest::post().uri("/v3/pat").set_json(json!({
|
test::TestRequest::post()
|
||||||
"scopes": 1,
|
.uri("/_internal/pat")
|
||||||
"name": "test_pat_scopes Name",
|
.set_json(json!({
|
||||||
"expires": Utc::now() + Duration::days(1),
|
"scopes": 1,
|
||||||
}))
|
"name": "test_pat_scopes Name",
|
||||||
|
"expires": Utc::now() + Duration::days(1),
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
let (_, success) = ScopeTest::new(&test_env)
|
let (_, success) = ScopeTest::new(&test_env)
|
||||||
.test(req_gen, pat_create)
|
.test(req_gen, pat_create)
|
||||||
@@ -1033,7 +1035,7 @@ pub async fn pat_scopes() {
|
|||||||
let pat_write = Scopes::PAT_WRITE;
|
let pat_write = Scopes::PAT_WRITE;
|
||||||
let req_gen = || {
|
let req_gen = || {
|
||||||
test::TestRequest::patch()
|
test::TestRequest::patch()
|
||||||
.uri(&format!("/v3/pat/{pat_id}"))
|
.uri(&format!("/_internal/pat/{pat_id}"))
|
||||||
.set_json(json!({}))
|
.set_json(json!({}))
|
||||||
};
|
};
|
||||||
ScopeTest::new(&test_env)
|
ScopeTest::new(&test_env)
|
||||||
@@ -1043,7 +1045,7 @@ pub async fn pat_scopes() {
|
|||||||
|
|
||||||
// Pat read
|
// Pat read
|
||||||
let pat_read = Scopes::PAT_READ;
|
let pat_read = Scopes::PAT_READ;
|
||||||
let req_gen = || test::TestRequest::get().uri("/v3/pat");
|
let req_gen = || test::TestRequest::get().uri("/_internal/pat");
|
||||||
ScopeTest::new(&test_env)
|
ScopeTest::new(&test_env)
|
||||||
.test(req_gen, pat_read)
|
.test(req_gen, pat_read)
|
||||||
.await
|
.await
|
||||||
@@ -1051,7 +1053,7 @@ pub async fn pat_scopes() {
|
|||||||
|
|
||||||
// Pat delete
|
// Pat delete
|
||||||
let pat_delete = Scopes::PAT_DELETE;
|
let pat_delete = Scopes::PAT_DELETE;
|
||||||
let req_gen = || test::TestRequest::delete().uri(&format!("/v3/pat/{pat_id}"));
|
let req_gen = || test::TestRequest::delete().uri(&format!("/_internal/pat/{pat_id}"));
|
||||||
ScopeTest::new(&test_env)
|
ScopeTest::new(&test_env)
|
||||||
.test(req_gen, pat_delete)
|
.test(req_gen, pat_delete)
|
||||||
.await
|
.await
|
||||||
|
|||||||
Reference in New Issue
Block a user