Skip to main content

cloudillo_file/
tag.rs

1//! File tag management handlers
2
3use axum::{
4	extract::{Path, Query, State},
5	Json,
6};
7use serde::{Deserialize, Serialize};
8
9use crate::prelude::*;
10use cloudillo_core::extract::Auth;
11
12const TAG_FORBIDDEN_CHARS: &[char] = &[' ', ',', '#', '\t', '\n'];
13
14/// GET /tag - List all tags for the tenant
15#[derive(Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub struct ListTagsQuery {
18	pub prefix: Option<String>,
19	pub with_counts: Option<bool>,
20	pub limit: Option<u32>,
21}
22
23/// Response for list tags endpoint
24#[derive(Serialize)]
25pub struct ListTagsResponse {
26	pub tags: Vec<TagInfo>,
27}
28
29pub async fn list_tags(
30	State(app): State<App>,
31	Auth(auth): Auth,
32	Query(q): Query<ListTagsQuery>,
33) -> ClResult<Json<ListTagsResponse>> {
34	let with_counts = q.with_counts.unwrap_or(false);
35	let tags = app
36		.meta_adapter
37		.list_tags(auth.tn_id, q.prefix.as_deref(), with_counts, q.limit)
38		.await?;
39
40	Ok(Json(ListTagsResponse { tags }))
41}
42
43/// PUT /file/:fileId/tag/:tag - Add a tag to a file
44#[derive(Serialize)]
45pub struct TagResponse {
46	pub tags: Vec<String>,
47}
48
49pub async fn put_file_tag(
50	State(app): State<App>,
51	Auth(auth): Auth,
52	Path((file_id, tag)): Path<(String, String)>,
53) -> ClResult<Json<TagResponse>> {
54	// Validate tag - no forbidden characters
55	if tag.chars().any(|c| TAG_FORBIDDEN_CHARS.contains(&c)) {
56		return Err(Error::PermissionDenied);
57	}
58
59	let tags = app.meta_adapter.add_tag(auth.tn_id, &file_id, &tag).await?;
60
61	info!("User {} added tag {} to file {}", auth.id_tag, tag, file_id);
62
63	Ok(Json(TagResponse { tags }))
64}
65
66/// DELETE /file/:fileId/tag/:tag - Remove a tag from a file
67pub async fn delete_file_tag(
68	State(app): State<App>,
69	Auth(auth): Auth,
70	Path((file_id, tag)): Path<(String, String)>,
71) -> ClResult<Json<TagResponse>> {
72	let tags = app.meta_adapter.remove_tag(auth.tn_id, &file_id, &tag).await?;
73
74	info!("User {} removed tag {} from file {}", auth.id_tag, tag, file_id);
75
76	Ok(Json(TagResponse { tags }))
77}
78
79// vim: ts=4