a mini social media app for small communities
1module database
2
3import entity { User, Notification, Like, LikeCache, Post }
4import util
5
6// new_user creates a new user and returns their struct after creation.
7pub fn (app &DatabaseAccess) new_user(user User) ?User {
8 sql app.db {
9 insert user into User
10 } or {
11 eprintln('failed to insert user ${user}')
12 return none
13 }
14
15 println('reg: ${user.username}')
16
17 return app.get_user_by_name(user.username)
18}
19
20// set_username sets the given user's username, returns true if this succeeded
21// and false otherwise.
22pub fn (app &DatabaseAccess) set_username(user_id int, new_username string) bool {
23 sql app.db {
24 update User set username = new_username where id == user_id
25 } or {
26 eprintln('failed to update username for ${user_id}')
27 return false
28 }
29 return true
30}
31
32// set_password sets the given user's password, returns true if this succeeded
33// and false otherwise.
34pub fn (app &DatabaseAccess) set_password(user_id int, hashed_new_password string) bool {
35 sql app.db {
36 update User set password = hashed_new_password where id == user_id
37 } or {
38 eprintln('failed to update password for ${user_id}')
39 return false
40 }
41 return true
42}
43
44// set_nickname sets the given user's nickname, returns true if this succeeded
45// and false otherwise.
46pub fn (app &DatabaseAccess) set_nickname(user_id int, new_nickname ?string) bool {
47 sql app.db {
48 update User set nickname = new_nickname where id == user_id
49 } or {
50 eprintln('failed to update nickname for ${user_id}')
51 return false
52 }
53 return true
54}
55
56// set_muted sets the given user's muted status, returns true if this succeeded
57// and false otherwise.
58pub fn (app &DatabaseAccess) set_muted(user_id int, muted bool) bool {
59 sql app.db {
60 update User set muted = muted where id == user_id
61 } or {
62 eprintln('failed to update muted status for ${user_id}')
63 return false
64 }
65 return true
66}
67
68// set_automated sets the given user's automated status, returns true if this
69// succeeded and false otherwise.
70pub fn (app &DatabaseAccess) set_automated(user_id int, automated bool) bool {
71 sql app.db {
72 update User set automated = automated where id == user_id
73 } or {
74 eprintln('failed to update automated status for ${user_id}')
75 return false
76 }
77 return true
78}
79
80// set_theme sets the given user's theme url, returns true if this succeeded and
81// false otherwise.
82pub fn (app &DatabaseAccess) set_theme(user_id int, theme ?string) bool {
83 sql app.db {
84 update User set theme = theme where id == user_id
85 } or {
86 eprintln('failed to update theme url for ${user_id}')
87 return false
88 }
89 return true
90}
91
92// set_pronouns sets the given user's pronouns, returns true if this succeeded
93// and false otherwise.
94pub fn (app &DatabaseAccess) set_pronouns(user_id int, pronouns string) bool {
95 sql app.db {
96 update User set pronouns = pronouns where id == user_id
97 } or {
98 eprintln('failed to update pronouns for ${user_id}')
99 return false
100 }
101 return true
102}
103
104// set_bio sets the given user's bio, returns true if this succeeded and false
105// otherwise.
106pub fn (app &DatabaseAccess) set_bio(user_id int, bio string) bool {
107 sql app.db {
108 update User set bio = bio where id == user_id
109 } or {
110 eprintln('failed to update bio for ${user_id}')
111 return false
112 }
113 return true
114}
115
116// get_user_by_name gets a user by their username, returns none if the user was
117// not found.
118pub fn (app &DatabaseAccess) get_user_by_name(username string) ?User {
119 users := sql app.db {
120 select from User where username == username
121 } or { [] }
122 if users.len != 1 {
123 return none
124 }
125 return users[0]
126}
127
128// get_user_by_id gets a user by their id, returns none if the user was not
129// found.
130pub fn (app &DatabaseAccess) get_user_by_id(id int) ?User {
131 users := sql app.db {
132 select from User where id == id
133 } or { [] }
134 if users.len != 1 {
135 return none
136 }
137 return users[0]
138}
139
140// get_users returns all users.
141pub fn (app &DatabaseAccess) get_users() []User {
142 users := sql app.db {
143 select from User
144 } or { [] }
145 return users
146}
147
148// does_user_like_post returns true if a user likes the given post.
149pub fn (app &DatabaseAccess) does_user_like_post(user_id int, post_id int) bool {
150 likes := app.db.exec_param2('SELECT id, is_like FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] }
151 if likes.len > 1 {
152 // something is very wrong lol
153 eprintln('does_user_like_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})')
154 } else if likes.len == 0 {
155 return false
156 }
157 return util.or_throw(likes.first().vals[1]).bool()
158}
159
160// does_user_dislike_post returns true if a user dislikes the given post.
161pub fn (app &DatabaseAccess) does_user_dislike_post(user_id int, post_id int) bool {
162 likes := app.db.exec_param2('SELECT id, is_like FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] }
163 if likes.len > 1 {
164 // something is very wrong lol
165 eprintln('does_user_dislike_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})')
166 } else if likes.len == 0 {
167 return false
168 }
169 return !util.or_throw(likes.first().vals[1]).bool()
170}
171
172// does_user_like_or_dislike_post returns true if a user likes *or* dislikes the
173// given post.
174pub fn (app &DatabaseAccess) does_user_like_or_dislike_post(user_id int, post_id int) bool {
175 likes := app.db.exec_param2('SELECT id FROM "Like" WHERE user_id = $1 AND post_id = $2', user_id.str(), post_id.str()) or { [] }
176 if likes.len > 1 {
177 // something is very wrong lol
178 eprintln('does_user_like_or_dislike_post: a user somehow got two or more likes on the same post (user: ${user_id}, post: ${post_id})')
179 }
180 return likes.len == 1
181}
182
183// delete_user deletes the given user and their data, returns true if this
184// succeeded and false otherwise.
185pub fn (app &DatabaseAccess) delete_user(user_id int) bool {
186 sql app.db {
187 delete from User where id == user_id
188 delete from Like where user_id == user_id
189 delete from Notification where user_id == user_id
190 } or {
191 return false
192 }
193
194 // delete posts and their likes
195 posts_from_this_user := app.db.exec_param('SELECT id FROM "Post" WHERE author_id = $1', user_id.str()) or { [] }
196
197 for post in posts_from_this_user {
198 id := util.or_throw(post.vals[0]).int()
199 sql app.db {
200 delete from Like where post_id == id
201 delete from LikeCache where post_id == id
202 } or {
203 eprintln('failed to delete like cache for post during user deletion: ${id}')
204 }
205 }
206
207 sql app.db {
208 delete from Post where author_id == user_id
209 } or {
210 eprintln('failed to delete posts by deleting user: ${user_id}')
211 }
212
213 return true
214}
215
216// search_for_users searches for posts matching the given query.
217// todo: query options/filters, such as created-after:<date>, created-before:<date>, etc
218pub fn (app &DatabaseAccess) search_for_users(query string, limit int, offset int) []User {
219 queried_users := app.db.exec_param_many('SELECT * FROM search_for_users($1, $2, $3)', [query, limit.str(), offset.str()]) or {
220 eprintln('search_for_users error in app.db.error: ${err}')
221 []
222 }
223 users := queried_users.map(|it| User.from_row(it))
224 return users
225}