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:
parent
4bbc57b0dc
commit
2d92b08404
@ -1,14 +1,12 @@
|
||||
pub mod checks;
|
||||
pub mod email;
|
||||
pub mod flows;
|
||||
pub mod oauth;
|
||||
pub mod pats;
|
||||
pub mod session;
|
||||
mod templates;
|
||||
pub mod templates;
|
||||
pub mod validate;
|
||||
pub use checks::{
|
||||
filter_authorized_projects, filter_authorized_versions, is_authorized, is_authorized_version,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
// pub use pat::{generate_pat, PersonalAccessToken};
|
||||
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 crate::auth::session::get_session_metadata;
|
||||
use super::AuthProvider;
|
||||
use crate::auth::AuthenticationError;
|
||||
use crate::database::models::user_item;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::models::pats::Scopes;
|
||||
use crate::models::users::{Role, User, UserId, UserPayoutData};
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::routes::internal::session::get_session_metadata;
|
||||
use actix_web::HttpRequest;
|
||||
use chrono::Utc;
|
||||
use reqwest::header::{HeaderValue, AUTHORIZATION};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use super::ids::*;
|
||||
use crate::auth::oauth::uris::OAuthRedirectUris;
|
||||
use crate::auth::AuthProvider;
|
||||
use crate::database::models::DatabaseError;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::{auth::flows::AuthProvider, models::pats::Scopes};
|
||||
use crate::models::pats::Scopes;
|
||||
use chrono::Duration;
|
||||
use rand::distributions::Alphanumeric;
|
||||
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())
|
||||
.configure(routes::v2::config)
|
||||
.configure(routes::v3::config)
|
||||
.configure(routes::internal::config)
|
||||
.configure(routes::root_config)
|
||||
.default_service(web::get().wrap(default_cors()).to(routes::not_found));
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use super::ids::Base62Id;
|
||||
use crate::auth::flows::AuthProvider;
|
||||
use crate::bitflags_serde_impl;
|
||||
use crate::{auth::AuthProvider, bitflags_serde_impl};
|
||||
use chrono::{DateTime, Utc};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::auth::session::SessionMetadata;
|
||||
use crate::database::models::pat_item::PersonalAccessToken;
|
||||
use crate::database::models::session_item::Session;
|
||||
use crate::database::models::{DatabaseError, OAuthAccessTokenId, PatId, SessionId, UserId};
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::routes::internal::session::SessionMetadata;
|
||||
use chrono::Utc;
|
||||
use itertools::Itertools;
|
||||
use sqlx::PgPool;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
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::{get_user_from_headers, AuthenticationError};
|
||||
use crate::auth::{get_user_from_headers, AuthProvider, AuthenticationError};
|
||||
use crate::database::models::flow_item::Flow;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::file_hosting::FileHost;
|
||||
@ -11,6 +10,7 @@ use crate::models::pats::Scopes;
|
||||
use crate::models::users::{Badges, Role};
|
||||
use crate::queue::session::AuthQueue;
|
||||
use crate::queue::socket::ActiveSockets;
|
||||
use crate::routes::internal::session::issue_session;
|
||||
use crate::routes::ApiError;
|
||||
use crate::util::captcha::check_turnstile_captcha;
|
||||
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)]
|
||||
pub struct TempUser {
|
||||
pub id: String,
|
||||
@ -1146,7 +1133,7 @@ pub async fn auth_callback(
|
||||
client: Data<PgPool>,
|
||||
file_host: Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
redis: Data<RedisPool>,
|
||||
) -> Result<HttpResponse, super::templates::ErrorPage> {
|
||||
) -> Result<HttpResponse, crate::auth::templates::ErrorPage> {
|
||||
let state_string = query
|
||||
.get("state")
|
||||
.ok_or_else(|| AuthenticationError::InvalidCredentials)?
|
||||
@ -1263,7 +1250,7 @@ pub async fn auth_callback(
|
||||
|
||||
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"),
|
||||
name: &user.username,
|
||||
}.render());
|
||||
@ -1322,7 +1309,7 @@ pub async fn auth_callback(
|
||||
.await.map_err(|_| AuthenticationError::SocketError)?;
|
||||
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"),
|
||||
name: &user.username,
|
||||
}.render());
|
||||
@ -2356,7 +2343,7 @@ fn send_email_verify(
|
||||
email: String,
|
||||
flow: String,
|
||||
opener: &str,
|
||||
) -> Result<(), super::email::MailError> {
|
||||
) -> Result<(), crate::auth::email::MailError> {
|
||||
send_email(
|
||||
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 futures::FutureExt;
|
||||
|
||||
pub mod internal;
|
||||
pub mod v2;
|
||||
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 analytics_get;
|
||||
mod collections;
|
||||
mod images;
|
||||
mod moderation;
|
||||
mod notifications;
|
||||
mod organizations;
|
||||
pub(crate) mod project_creation;
|
||||
mod projects;
|
||||
mod reports;
|
||||
@ -25,17 +21,13 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
actix_web::web::scope("v2")
|
||||
.wrap(default_cors())
|
||||
.configure(admin::config)
|
||||
.configure(analytics_get::config)
|
||||
// Todo: separate these- they need to also follow v2-v3 conversion
|
||||
.configure(crate::auth::session::config)
|
||||
.configure(crate::auth::flows::config)
|
||||
.configure(crate::auth::pats::config)
|
||||
.configure(super::internal::session::config)
|
||||
.configure(super::internal::flows::config)
|
||||
.configure(super::internal::pats::config)
|
||||
.configure(moderation::config)
|
||||
.configure(notifications::config)
|
||||
.configure(organizations::config)
|
||||
.configure(project_creation::config)
|
||||
.configure(collections::config)
|
||||
.configure(images::config)
|
||||
.configure(projects::config)
|
||||
.configure(reports::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
|
||||
}
|
||||
|
||||
#[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)
|
||||
#[get("{id}/members")]
|
||||
pub async fn team_members_get(
|
||||
|
||||
@ -20,9 +20,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("user")
|
||||
.service(user_get)
|
||||
.service(orgs_list)
|
||||
.service(projects_list)
|
||||
.service(collections_list)
|
||||
.service(user_delete)
|
||||
.service(user_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! {
|
||||
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 serde_json::json;
|
||||
|
||||
pub mod admin;
|
||||
pub mod analytics_get;
|
||||
pub mod collections;
|
||||
pub mod images;
|
||||
@ -29,13 +28,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("v3")
|
||||
.wrap(default_cors())
|
||||
.configure(admin::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(images::config)
|
||||
.configure(moderation::config)
|
||||
|
||||
@ -41,7 +41,7 @@ impl Api for ApiV3 {
|
||||
|
||||
async fn reset_search_index(&self) -> ServiceResponse {
|
||||
let req = actix_web::test::TestRequest::post()
|
||||
.uri("/v3/admin/_force_reindex")
|
||||
.uri("/_internal/admin/_force_reindex")
|
||||
.append_header((
|
||||
"Modrinth-Admin",
|
||||
dotenvy::var("LABRINTH_ADMIN_KEY").unwrap(),
|
||||
|
||||
@ -55,7 +55,7 @@ impl ApiV3 {
|
||||
pub async fn oauth_accept(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/oauth/accept")
|
||||
.uri("/_internal/oauth/accept")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.set_json(RespondToOAuthClientScopes {
|
||||
flow: flow.to_string(),
|
||||
@ -68,7 +68,7 @@ impl ApiV3 {
|
||||
pub async fn oauth_reject(&self, flow: &str, pat: &str) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/oauth/reject")
|
||||
.uri("/_internal/oauth/reject")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.set_json(RespondToOAuthClientScopes {
|
||||
flow: flow.to_string(),
|
||||
@ -87,7 +87,7 @@ impl ApiV3 {
|
||||
) -> ServiceResponse {
|
||||
self.call(
|
||||
TestRequest::post()
|
||||
.uri("/v3/oauth/token")
|
||||
.uri("/_internal/oauth/token")
|
||||
.append_header((AUTHORIZATION, client_secret))
|
||||
.set_form(TokenRequest {
|
||||
grant_type: "authorization_code".to_string(),
|
||||
@ -108,7 +108,7 @@ pub fn generate_authorize_uri(
|
||||
state: Option<&str>,
|
||||
) -> String {
|
||||
format!(
|
||||
"/v3/oauth/authorize?client_id={}{}{}{}",
|
||||
"/_internal/oauth/authorize?client_id={}{}{}{}",
|
||||
urlencoding::encode(client_id),
|
||||
optional_query_param("redirect_uri", redirect_uri),
|
||||
optional_query_param("scope", scope),
|
||||
|
||||
@ -27,7 +27,7 @@ impl ApiV3 {
|
||||
) -> ServiceResponse {
|
||||
let max_scopes = max_scopes.bits();
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/oauth/app")
|
||||
.uri("/_internal/oauth/app")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.set_json(json!({
|
||||
"name": name,
|
||||
@ -52,7 +52,7 @@ impl ApiV3 {
|
||||
|
||||
pub async fn get_oauth_client(&self, client_id: String, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/oauth/app/{}", client_id))
|
||||
.uri(&format!("/_internal/oauth/app/{}", client_id))
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
|
||||
@ -66,7 +66,10 @@ impl ApiV3 {
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
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)
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
@ -76,7 +79,7 @@ impl ApiV3 {
|
||||
|
||||
pub async fn delete_oauth_client(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::delete()
|
||||
.uri(&format!("/v3/oauth/app/{}", client_id))
|
||||
.uri(&format!("/_internal/oauth/app/{}", client_id))
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
|
||||
@ -86,7 +89,7 @@ impl ApiV3 {
|
||||
pub async fn revoke_oauth_authorization(&self, client_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = TestRequest::delete()
|
||||
.uri(&format!(
|
||||
"/v3/oauth/authorizations?client_id={}",
|
||||
"/_internal/oauth/authorizations?client_id={}",
|
||||
urlencoding::encode(client_id)
|
||||
))
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
@ -96,7 +99,7 @@ impl ApiV3 {
|
||||
|
||||
pub async fn get_user_oauth_authorizations(&self, pat: &str) -> Vec<OAuthClientAuthorization> {
|
||||
let req = TestRequest::get()
|
||||
.uri("/v3/oauth/authorizations")
|
||||
.uri("/_internal/oauth/authorizations")
|
||||
.append_header((AUTHORIZATION, pat))
|
||||
.to_request();
|
||||
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 {
|
||||
// Create a PAT for a full test
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||
@ -43,7 +43,7 @@ pub async fn pat_full_test() {
|
||||
// Get PAT again
|
||||
let req = test::TestRequest::get()
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status().as_u16(), 200);
|
||||
@ -75,7 +75,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change scopes and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": 0,
|
||||
@ -87,7 +87,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change scopes back, and set expiry to the past, and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE,
|
||||
@ -103,7 +103,7 @@ pub async fn pat_full_test() {
|
||||
|
||||
// Change everything back to normal and test again
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"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
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"expires": Utc::now() - Duration::days(1), // Past
|
||||
@ -132,7 +132,7 @@ pub async fn pat_full_test() {
|
||||
}
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
@ -148,7 +148,7 @@ pub async fn pat_full_test() {
|
||||
// Delete PAT
|
||||
let req = test::TestRequest::delete()
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
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 {
|
||||
// Creating a PAT with no name should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"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
|
||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"name": name,
|
||||
@ -189,7 +189,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Creating a PAT with an expiry in the past should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE, // Collection create as an easily tested example
|
||||
@ -207,7 +207,7 @@ pub async fn bad_pats() {
|
||||
continue;
|
||||
}
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
@ -224,7 +224,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Create a 'good' PAT for patching
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": Scopes::COLLECTION_CREATE,
|
||||
@ -240,7 +240,7 @@ pub async fn bad_pats() {
|
||||
// Patching to a bad name should fail
|
||||
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/pat")
|
||||
.uri("/_internal/pat")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"name": name,
|
||||
@ -252,7 +252,7 @@ pub async fn bad_pats() {
|
||||
|
||||
// Patching to a bad expiry should fail
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"expires": Utc::now() - Duration::days(1), // Past
|
||||
@ -269,7 +269,7 @@ pub async fn bad_pats() {
|
||||
}
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{}", id))
|
||||
.uri(&format!("/_internal/pat/{}", id))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"scopes": scope.bits(),
|
||||
|
||||
@ -1017,11 +1017,13 @@ pub async fn pat_scopes() {
|
||||
// Pat create
|
||||
let pat_create = Scopes::PAT_CREATE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::post().uri("/v3/pat").set_json(json!({
|
||||
"scopes": 1,
|
||||
"name": "test_pat_scopes Name",
|
||||
"expires": Utc::now() + Duration::days(1),
|
||||
}))
|
||||
test::TestRequest::post()
|
||||
.uri("/_internal/pat")
|
||||
.set_json(json!({
|
||||
"scopes": 1,
|
||||
"name": "test_pat_scopes Name",
|
||||
"expires": Utc::now() + Duration::days(1),
|
||||
}))
|
||||
};
|
||||
let (_, success) = ScopeTest::new(&test_env)
|
||||
.test(req_gen, pat_create)
|
||||
@ -1033,7 +1035,7 @@ pub async fn pat_scopes() {
|
||||
let pat_write = Scopes::PAT_WRITE;
|
||||
let req_gen = || {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v3/pat/{pat_id}"))
|
||||
.uri(&format!("/_internal/pat/{pat_id}"))
|
||||
.set_json(json!({}))
|
||||
};
|
||||
ScopeTest::new(&test_env)
|
||||
@ -1043,7 +1045,7 @@ pub async fn pat_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)
|
||||
.test(req_gen, pat_read)
|
||||
.await
|
||||
@ -1051,7 +1053,7 @@ pub async fn pat_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)
|
||||
.test(req_gen, pat_delete)
|
||||
.await
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user