Skip to main content

max / makenotwork

4.1 KB · 132 lines History Blame Raw
1 //! The admin user list and its counts.
2 //!
3 //! `get_all_users` and `count_users` are two `match` ladders over the same
4 //! filter set, and a filter added to one and not the other silently mispages
5 //! the list. Their agreement is enforced by sitting next to each other, which
6 //! sixty lines apart in a 1330-line file was not.
7
8 use sqlx::PgPool;
9
10 use crate::db::models::DbUser;
11 use crate::error::Result;
12
13 /// Admin query: all users, optionally filtered by suspension status, with pagination.
14 #[tracing::instrument(skip_all)]
15 pub async fn get_all_users(
16 pool: &PgPool,
17 filter: Option<&str>,
18 limit: i64,
19 offset: i64,
20 ) -> Result<Vec<DbUser>> {
21 let limit = limit.min(200);
22 let users = match filter {
23 Some("suspended") => {
24 sqlx::query_as::<_, DbUser>(
25 "SELECT * FROM users WHERE suspended_at IS NOT NULL ORDER BY suspended_at DESC LIMIT $1 OFFSET $2",
26 )
27 .bind(limit)
28 .bind(offset)
29 .fetch_all(pool)
30 .await?
31 }
32 Some("active") => {
33 sqlx::query_as::<_, DbUser>(
34 "SELECT * FROM users WHERE suspended_at IS NULL ORDER BY created_at DESC LIMIT $1 OFFSET $2",
35 )
36 .bind(limit)
37 .bind(offset)
38 .fetch_all(pool)
39 .await?
40 }
41 // Creators with a custom page; most recently changed first, so
42 // "recently changed" surfaces naturally at the top.
43 Some("custom_pages") => {
44 sqlx::query_as::<_, DbUser>(
45 "SELECT * FROM users WHERE custom_html <> '' OR custom_css <> '' \
46 ORDER BY custom_pages_updated_at DESC NULLS LAST, created_at DESC LIMIT $1 OFFSET $2",
47 )
48 .bind(limit)
49 .bind(offset)
50 .fetch_all(pool)
51 .await?
52 }
53 Some("pages_locked") => {
54 sqlx::query_as::<_, DbUser>(
55 "SELECT * FROM users WHERE custom_pages_locked = true ORDER BY created_at DESC LIMIT $1 OFFSET $2",
56 )
57 .bind(limit)
58 .bind(offset)
59 .fetch_all(pool)
60 .await?
61 }
62 _ => {
63 sqlx::query_as::<_, DbUser>(
64 "SELECT * FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2",
65 )
66 .bind(limit)
67 .bind(offset)
68 .fetch_all(pool)
69 .await?
70 }
71 };
72
73 Ok(users)
74 }
75
76 /// Count users matching a filter (for pagination totals).
77 #[tracing::instrument(skip_all)]
78 pub async fn count_users(pool: &PgPool, filter: Option<&str>) -> Result<i64> {
79 let count = match filter {
80 Some("suspended") => {
81 sqlx::query_scalar::<_, i64>(
82 "SELECT COUNT(*) FROM users WHERE suspended_at IS NOT NULL",
83 )
84 .fetch_one(pool)
85 .await?
86 }
87 Some("active") => {
88 sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM users WHERE suspended_at IS NULL")
89 .fetch_one(pool)
90 .await?
91 }
92 Some("custom_pages") => {
93 sqlx::query_scalar::<_, i64>(
94 "SELECT COUNT(*) FROM users WHERE custom_html <> '' OR custom_css <> ''",
95 )
96 .fetch_one(pool)
97 .await?
98 }
99 Some("pages_locked") => {
100 sqlx::query_scalar::<_, i64>(
101 "SELECT COUNT(*) FROM users WHERE custom_pages_locked = true",
102 )
103 .fetch_one(pool)
104 .await?
105 }
106 _ => {
107 sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM users")
108 .fetch_one(pool)
109 .await?
110 }
111 };
112
113 Ok(count)
114 }
115
116 /// Count total and suspended users in a single query.
117 #[tracing::instrument(skip_all)]
118 pub async fn count_users_summary(pool: &PgPool) -> Result<(i64, i64)> {
119 let (total, suspended): (i64, i64) = sqlx::query_as(
120 r"
121 SELECT
122 COUNT(*),
123 COUNT(*) FILTER (WHERE suspended_at IS NOT NULL)
124 FROM users
125 ",
126 )
127 .fetch_one(pool)
128 .await?;
129
130 Ok((total, suspended))
131 }
132