Version slugs (#533)

* Version slugs

* Get rid of new field, finish it up
This commit is contained in:
Geometrically
2023-02-15 13:38:37 -07:00
committed by GitHub
parent 8eb9fb1834
commit b056610eaa
6 changed files with 124 additions and 35 deletions

View File

@@ -1,5 +1,6 @@
use super::ids::*;
use super::DatabaseError;
use crate::models::ids::base62_impl::parse_base62;
use crate::models::projects::{FileType, VersionStatus, VersionType};
use chrono::{DateTime, Utc};
use serde::Deserialize;
@@ -499,7 +500,7 @@ impl Version {
INNER JOIN loaders_versions lv ON lv.version_id = v.id
INNER JOIN loaders l on lv.loader_id = l.id AND (cardinality($3::varchar[]) = 0 OR l.loader = ANY($3::varchar[]))
WHERE v.mod_id = $1 AND ($4::varchar IS NULL OR v.version_type = $4)
ORDER BY v.date_published, v.id ASC
ORDER BY v.date_published, v.id DESC
LIMIT $5 OFFSET $6
",
project_id as ProjectId,
@@ -905,6 +906,39 @@ impl Version {
.try_collect::<Vec<QueryVersion>>()
.await
}
pub async fn get_full_from_id_slug<'a, 'b, E>(
project_id_or_slug: &str,
slug: &str,
executor: E,
) -> Result<Option<QueryVersion>, sqlx::error::Error>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
{
let project_id_opt =
parse_base62(project_id_or_slug).ok().map(|x| x as i64);
let id_opt = parse_base62(slug).ok().map(|x| x as i64);
let id = sqlx::query!(
"
SELECT v.id FROM versions v
INNER JOIN mods m ON mod_id = m.id
WHERE (m.id = $1 OR m.slug = $2) AND (v.id = $3 OR v.version_number = $4)
ORDER BY date_published ASC
",
project_id_opt,
project_id_or_slug,
id_opt,
slug
)
.fetch_optional(executor)
.await?;
if let Some(version_id) = id {
Version::get_full(VersionId(version_id.id), executor).await
} else {
Ok(None)
}
}
}
#[derive(Clone)]

View File

@@ -277,6 +277,12 @@ async fn main() -> std::io::Result<()> {
dotenvy::var("RATE_LIMIT_IGNORE_KEY").ok(),
),
)
.app_data(web::FormConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
}))
.app_data(web::PathConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
}))
.app_data(web::QueryConfig::default().error_handler(|err, _req| {
routes::ApiError::Validation(err.to_string()).into()
}))

View File

@@ -75,7 +75,8 @@ pub fn projects_config(cfg: &mut web::ServiceConfig) {
.service(
web::scope("{project_id}")
.service(versions::version_list)
.service(projects::dependency_list),
.service(projects::dependency_list)
.service(versions::version_project_get),
),
);
}

View File

@@ -308,7 +308,7 @@ pub async fn get_update_from_hash(
)
.await?;
if let Some(version_id) = version_ids.last() {
if let Some(version_id) = version_ids.first() {
let version_data =
database::models::Version::get_full(*version_id, &**pool)
.await?;
@@ -503,7 +503,7 @@ pub async fn update_files(
)
.await?;
if let Some(latest_version) = updated_versions.last() {
if let Some(latest_version) = updated_versions.first() {
let mut version_ids = version_ids.write().await;
version_ids.insert(*latest_version, row.hash);

View File

@@ -144,6 +144,31 @@ pub async fn version_list(
}
}
// Given a project ID/slug and a version slug
#[get("version/{slug}")]
pub async fn version_project_get(
req: HttpRequest,
info: web::Path<(String, String)>,
pool: web::Data<PgPool>,
) -> Result<HttpResponse, ApiError> {
let id = info.into_inner();
let version_data =
database::models::Version::get_full_from_id_slug(&id.0, &id.1, &**pool)
.await?;
let user_option = get_user_from_headers(req.headers(), &**pool).await.ok();
if let Some(data) = version_data {
if is_authorized_version(&data.inner, &user_option, &pool).await? {
return Ok(
HttpResponse::Ok().json(models::projects::Version::from(data))
);
}
}
Ok(HttpResponse::NotFound().body(""))
}
#[derive(Serialize, Deserialize)]
pub struct VersionIds {
pub ids: String,