use crate::permissions::Perm;
use crate::Config;
use axum::{
extract::State,
response::{IntoResponse, Redirect, Response},
routing::{get, post},
Form, Router,
};
use axum_extra::extract::SignedCookieJar;
use diesel::{OptionalExtension, QueryDsl};
use diesel_async::RunQueryDsl;
use serde_derive::*;
use tracing::*;
mod get;
mod list;
use diesel::ExpressionMethods;
use libpijul;
use libpijul::Base32;
pub fn router() -> Router<Config> {
Router::new()
.route("/{owner}/{repo}/list", get(list::changelist))
.route("/{owner}/{repo}/get/{hash}", get(get::change))
.route("/{owner}/{repo}/unrecord", post(unrecord))
.fallback(crate::fallback_json)
}
#[derive(Debug, Clone)]
pub struct ChangeAuthor {
pub login: String,
}
pub async fn change_author(
db: &crate::config::Db,
author: &libpijul::change::Author,
) -> Result<ChangeAuthor, crate::Error> {
debug!("change_author: {:?}", author);
use crate::db::users::dsl as u;
if let Some(key) = author.0.get("key") {
let keyb = bs58::decode(key.as_bytes()).into_vec();
if let Ok(keyb) = keyb {
use crate::db::signingkeys::dsl as sk;
if let Some(login) = u::users
.inner_join(sk::signingkeys)
.filter(sk::public_key.eq(keyb))
.filter(u::is_active)
.select(u::login)
.get_result(&mut db.get().await?)
.await
.optional()?
{
return Ok(ChangeAuthor {
login,
});
}
}
Ok(ChangeAuthor {
login: key.to_string(),
})
} else if let Some(name) = author.0.get("name") {
Ok(ChangeAuthor {
login: name.to_string(),
})
} else {
Ok(ChangeAuthor {
login: "?".to_string(),
})
}
}
pub async fn authors_string(
db: &crate::config::Db,
authors: &[libpijul::change::Author],
) -> Result<Vec<String>, crate::Error> {
let mut result = Vec::new();
for a in authors {
result.push(change_author(db, &a).await?.login)
}
Ok(result)
}
#[derive(Debug, Default, Serialize, Clone)]
pub struct Author {
#[serde(skip_serializing_if = "Option::is_none")]
login: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
key: String,
}
pub async fn get_authors(
db: &mut diesel_async::AsyncPgConnection,
authors: &mut [libpijul::change::Author],
) -> Result<Vec<Author>, diesel::result::Error> {
let mut auth = Vec::with_capacity(authors.len());
for a in authors.iter_mut() {
if let Some(key) = a.0.remove("key") {
use crate::db::signingkeys::dsl as k;
use crate::db::users::dsl as u;
if let Some((login, name)) = k::signingkeys
.find(&bs58::decode(&key).into_vec().unwrap())
.inner_join(u::users)
.select((u::login, u::name))
.get_result::<(String, Option<String>)>(db)
.await
.optional()?
{
auth.push(Author {
key,
login: Some(login),
name,
})
} else {
auth.push(Author {
key,
..Author::default()
})
}
}
}
Ok(auth)
}
#[derive(Debug, Deserialize)]
pub struct UnrecordForm {
token: String,
hash: String,
}
async fn unrecord(
State(config): State<Config>,
jar: SignedCookieJar,
token: axum_csrf::CsrfToken,
repo: crate::repository::RepoPath,
Form(form): Form<UnrecordForm>,
) -> Result<Response, crate::Error> {
token.verify(&form.token)?;
let uid = crate::get_user_id_strict(&jar)?;
let mut db = config.db.get().await.unwrap();
let (id, _) = crate::repository::repository_id(&mut db, &repo.owner, &repo.repo, Some(uid), Perm::APPLY)
.await?;
config
.replicator
.handle_update(
None,
None,
None,
::replication::Update::Unrecord {
repo: id,
channel: repo.channel.unwrap_or_else(|| "main".to_string()),
hash: if let Some(h) = libpijul::Hash::from_base32(form.hash.as_bytes()) {
h
} else {
return Err(crate::Error::HashParse { hash: form.hash });
},
deps: config.replicator.deps(id).await?,
},
)
.await?;
Ok(Redirect::to(&format!("/{}/{}/change", repo.owner, repo.repo)).into_response())
}