180 lines
5.2 KiB
Rust
180 lines
5.2 KiB
Rust
#![allow(clippy::missing_errors_doc)]
|
|
#![allow(clippy::unnecessary_struct_initialization)]
|
|
#![allow(clippy::unused_async)]
|
|
use axum::http::HeaderMap;
|
|
use loco_rs::prelude::*;
|
|
use sea_orm::{ColumnTrait, QueryFilter, QueryOrder};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::controllers::admin::check_auth;
|
|
use crate::models::_entities::friend_links::{ActiveModel, Column, Entity, Model};
|
|
use crate::services::{admin_audit, notifications};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct Params {
|
|
pub site_name: Option<String>,
|
|
pub site_url: String,
|
|
pub avatar_url: Option<String>,
|
|
pub description: Option<String>,
|
|
pub category: Option<String>,
|
|
pub status: Option<String>,
|
|
}
|
|
|
|
impl Params {
|
|
fn update(&self, item: &mut ActiveModel) {
|
|
item.site_url = Set(self.site_url.clone());
|
|
if let Some(site_name) = &self.site_name {
|
|
item.site_name = Set(Some(site_name.clone()));
|
|
}
|
|
if let Some(avatar_url) = &self.avatar_url {
|
|
item.avatar_url = Set(Some(avatar_url.clone()));
|
|
}
|
|
if let Some(description) = &self.description {
|
|
item.description = Set(Some(description.clone()));
|
|
}
|
|
if let Some(category) = &self.category {
|
|
item.category = Set(Some(category.clone()));
|
|
}
|
|
if let Some(status) = &self.status {
|
|
item.status = Set(Some(status.clone()));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, Deserialize)]
|
|
pub struct ListQuery {
|
|
pub status: Option<String>,
|
|
pub category: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize)]
|
|
pub struct CreateFriendLinkRequest {
|
|
#[serde(default, alias = "siteName")]
|
|
pub site_name: Option<String>,
|
|
#[serde(alias = "siteUrl")]
|
|
pub site_url: String,
|
|
#[serde(default, alias = "avatarUrl")]
|
|
pub avatar_url: Option<String>,
|
|
#[serde(default)]
|
|
pub description: Option<String>,
|
|
#[serde(default)]
|
|
pub category: Option<String>,
|
|
#[serde(default)]
|
|
pub status: Option<String>,
|
|
}
|
|
|
|
async fn load_item(ctx: &AppContext, id: i32) -> Result<Model> {
|
|
let item = Entity::find_by_id(id).one(&ctx.db).await?;
|
|
item.ok_or_else(|| Error::NotFound)
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn list(
|
|
Query(query): Query<ListQuery>,
|
|
State(ctx): State<AppContext>,
|
|
headers: HeaderMap,
|
|
) -> Result<Response> {
|
|
let authenticated = check_auth(&headers).ok();
|
|
let mut db_query = Entity::find().order_by_desc(Column::CreatedAt);
|
|
|
|
if let Some(status) = query.status {
|
|
db_query = db_query.filter(Column::Status.eq(status));
|
|
} else if authenticated.is_none() {
|
|
db_query = db_query.filter(Column::Status.eq("approved"));
|
|
}
|
|
|
|
if let Some(category) = query.category {
|
|
db_query = db_query.filter(Column::Category.eq(category));
|
|
}
|
|
|
|
format::json(db_query.all(&ctx.db).await?)
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn add(
|
|
State(ctx): State<AppContext>,
|
|
Json(params): Json<CreateFriendLinkRequest>,
|
|
) -> Result<Response> {
|
|
let mut item = ActiveModel {
|
|
..Default::default()
|
|
};
|
|
item.site_name = Set(params.site_name);
|
|
item.site_url = Set(params.site_url);
|
|
item.avatar_url = Set(params.avatar_url);
|
|
item.description = Set(params.description);
|
|
item.category = Set(params.category);
|
|
item.status = Set(Some(params.status.unwrap_or_else(|| "pending".to_string())));
|
|
let item = item.insert(&ctx.db).await?;
|
|
notifications::notify_new_friend_link(&ctx, &item).await;
|
|
format::json(item)
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn update(
|
|
headers: HeaderMap,
|
|
Path(id): Path<i32>,
|
|
State(ctx): State<AppContext>,
|
|
Json(params): Json<Params>,
|
|
) -> Result<Response> {
|
|
let actor = check_auth(&headers)?;
|
|
let item = load_item(&ctx, id).await?;
|
|
let mut item = item.into_active_model();
|
|
params.update(&mut item);
|
|
let item = item.update(&ctx.db).await?;
|
|
admin_audit::log_event(
|
|
&ctx,
|
|
Some(&actor),
|
|
"friend_link.update",
|
|
"friend_link",
|
|
Some(item.id.to_string()),
|
|
item.site_name.clone().or_else(|| Some(item.site_url.clone())),
|
|
Some(serde_json::json!({ "status": item.status })),
|
|
)
|
|
.await?;
|
|
format::json(item)
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn remove(
|
|
headers: HeaderMap,
|
|
Path(id): Path<i32>,
|
|
State(ctx): State<AppContext>,
|
|
) -> Result<Response> {
|
|
let actor = check_auth(&headers)?;
|
|
let item = load_item(&ctx, id).await?;
|
|
let label = item.site_name.clone().or_else(|| Some(item.site_url.clone()));
|
|
item.delete(&ctx.db).await?;
|
|
admin_audit::log_event(
|
|
&ctx,
|
|
Some(&actor),
|
|
"friend_link.delete",
|
|
"friend_link",
|
|
Some(id.to_string()),
|
|
label,
|
|
None,
|
|
)
|
|
.await?;
|
|
format::empty()
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn get_one(
|
|
headers: HeaderMap,
|
|
Path(id): Path<i32>,
|
|
State(ctx): State<AppContext>,
|
|
) -> Result<Response> {
|
|
check_auth(&headers)?;
|
|
format::json(load_item(&ctx, id).await?)
|
|
}
|
|
|
|
pub fn routes() -> Routes {
|
|
Routes::new()
|
|
.prefix("api/friend_links/")
|
|
.add("/", get(list))
|
|
.add("/", post(add))
|
|
.add("{id}", get(get_one))
|
|
.add("{id}", delete(remove))
|
|
.add("{id}", put(update))
|
|
.add("{id}", patch(update))
|
|
}
|