Skip to main content

systemprompt_files/repository/file/
stats.rs

1//! File storage statistics queries.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::FileRepository;
7use crate::error::FilesResult;
8
9#[derive(Debug, Clone, Copy)]
10pub struct FileStats {
11    pub total_files: i64,
12    pub total_size_bytes: i64,
13    pub ai_images_count: i64,
14    pub image_count: i64,
15    pub image_size_bytes: i64,
16    pub document_count: i64,
17    pub document_size_bytes: i64,
18    pub audio_count: i64,
19    pub audio_size_bytes: i64,
20    pub video_count: i64,
21    pub video_size_bytes: i64,
22    pub other_count: i64,
23    pub other_size_bytes: i64,
24}
25
26impl FileRepository {
27    pub async fn get_stats(&self) -> FilesResult<FileStats> {
28        let row = sqlx::query!(
29            r#"
30            SELECT
31                COUNT(*) as "total_files!",
32                COALESCE(SUM(size_bytes), 0)::bigint as "total_size_bytes!",
33                COUNT(*) FILTER (WHERE ai_content = true) as "ai_images_count!",
34                COUNT(*) FILTER (WHERE mime_type LIKE 'image/%') as "image_count!",
35                COALESCE(SUM(size_bytes) FILTER (WHERE mime_type LIKE 'image/%'), 0)::bigint as "image_size!",
36                COUNT(*) FILTER (WHERE mime_type LIKE 'application/pdf' OR mime_type LIKE 'application/msword%' OR mime_type LIKE 'application/vnd.openxmlformats%' OR mime_type LIKE 'text/%') as "document_count!",
37                COALESCE(SUM(size_bytes) FILTER (WHERE mime_type LIKE 'application/pdf' OR mime_type LIKE 'application/msword%' OR mime_type LIKE 'application/vnd.openxmlformats%' OR mime_type LIKE 'text/%'), 0)::bigint as "document_size!",
38                COUNT(*) FILTER (WHERE mime_type LIKE 'audio/%') as "audio_count!",
39                COALESCE(SUM(size_bytes) FILTER (WHERE mime_type LIKE 'audio/%'), 0)::bigint as "audio_size!",
40                COUNT(*) FILTER (WHERE mime_type LIKE 'video/%') as "video_count!",
41                COALESCE(SUM(size_bytes) FILTER (WHERE mime_type LIKE 'video/%'), 0)::bigint as "video_size!"
42            FROM files
43            WHERE deleted_at IS NULL
44            "#
45        )
46        .fetch_one(self.pool.as_ref())
47        .await?;
48
49        let image_count = row.image_count;
50        let document_count = row.document_count;
51        let audio_count = row.audio_count;
52        let video_count = row.video_count;
53        let other_count =
54            (row.total_files - image_count - document_count - audio_count - video_count).max(0);
55
56        let image_size = row.image_size;
57        let document_size = row.document_size;
58        let audio_size = row.audio_size;
59        let video_size = row.video_size;
60        let other_size =
61            (row.total_size_bytes - image_size - document_size - audio_size - video_size).max(0);
62
63        Ok(FileStats {
64            total_files: row.total_files,
65            total_size_bytes: row.total_size_bytes,
66            ai_images_count: row.ai_images_count,
67            image_count,
68            image_size_bytes: image_size,
69            document_count,
70            document_size_bytes: document_size,
71            audio_count,
72            audio_size_bytes: audio_size,
73            video_count,
74            video_size_bytes: video_size,
75            other_count,
76            other_size_bytes: other_size,
77        })
78    }
79}