Skip to main content

systemprompt_users/repository/banned_ip/
listing.rs

1//! Banned-IP listing queries.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use crate::error::Result;
7use systemprompt_identifiers::SessionId;
8
9use super::BannedIpRepository;
10use super::types::BannedIp;
11
12impl BannedIpRepository {
13    pub async fn list_active_bans(&self, limit: i64) -> Result<Vec<BannedIp>> {
14        let bans = sqlx::query_as!(
15            BannedIp,
16            r#"
17            SELECT
18                ip_address,
19                reason,
20                banned_at,
21                expires_at,
22                ban_count,
23                last_offense_path,
24                last_user_agent,
25                is_permanent,
26                source_fingerprint,
27                ban_source,
28                associated_session_ids as "associated_session_ids: Vec<SessionId>"
29            FROM banned_ips
30            WHERE expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP
31            ORDER BY banned_at DESC
32            LIMIT $1
33            "#,
34            limit
35        )
36        .fetch_all(&*self.pool)
37        .await?;
38
39        Ok(bans)
40    }
41
42    pub async fn list_bans_by_source(&self, ban_source: &str, limit: i64) -> Result<Vec<BannedIp>> {
43        let bans = sqlx::query_as!(
44            BannedIp,
45            r#"
46            SELECT
47                ip_address,
48                reason,
49                banned_at,
50                expires_at,
51                ban_count,
52                last_offense_path,
53                last_user_agent,
54                is_permanent,
55                source_fingerprint,
56                ban_source,
57                associated_session_ids as "associated_session_ids: Vec<SessionId>"
58            FROM banned_ips
59            WHERE ban_source = $1
60              AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)
61            ORDER BY banned_at DESC
62            LIMIT $2
63            "#,
64            ban_source,
65            limit
66        )
67        .fetch_all(&*self.pool)
68        .await?;
69
70        Ok(bans)
71    }
72
73    pub async fn list_bans_by_fingerprint(&self, fingerprint: &str) -> Result<Vec<BannedIp>> {
74        let bans = sqlx::query_as!(
75            BannedIp,
76            r#"
77            SELECT
78                ip_address,
79                reason,
80                banned_at,
81                expires_at,
82                ban_count,
83                last_offense_path,
84                last_user_agent,
85                is_permanent,
86                source_fingerprint,
87                ban_source,
88                associated_session_ids as "associated_session_ids: Vec<SessionId>"
89            FROM banned_ips
90            WHERE source_fingerprint = $1
91            ORDER BY banned_at DESC
92            "#,
93            fingerprint
94        )
95        .fetch_all(&*self.pool)
96        .await?;
97
98        Ok(bans)
99    }
100
101    pub async fn count_active_bans(&self) -> Result<i64> {
102        let result = sqlx::query_scalar!(
103            r#"
104            SELECT COUNT(*)::BIGINT as "count!"
105            FROM banned_ips
106            WHERE expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP
107            "#
108        )
109        .fetch_one(&*self.pool)
110        .await?;
111
112        Ok(result)
113    }
114}