a mini social media app for small communities

refactors in preparation for search

Changed files
+182 -79
src
+26
src/database/like.v
··· 2 3 import entity { Like, LikeCache } 4 5 // get_net_likes_for_post returns the net likes of the given post. 6 pub fn (app &DatabaseAccess) get_net_likes_for_post(post_id int) int { 7 // check cache ··· 43 44 return likes 45 }
··· 2 3 import entity { Like, LikeCache } 4 5 + // add_like adds a like to the database, returns true if this succeeds and false 6 + // otherwise. 7 + pub fn (app &DatabaseAccess) add_like(like &Like) bool { 8 + sql app.db { 9 + insert like into Like 10 + // yeet the old cached like value 11 + delete from LikeCache where post_id == like.post_id 12 + } or { 13 + return false 14 + } 15 + return true 16 + } 17 + 18 // get_net_likes_for_post returns the net likes of the given post. 19 pub fn (app &DatabaseAccess) get_net_likes_for_post(post_id int) int { 20 // check cache ··· 56 57 return likes 58 } 59 + 60 + // unlike_post removes a (dis)like from the given post, returns true if this 61 + // succeeds and false otherwise. 62 + pub fn (app &DatabaseAccess) unlike_post(post_id int, user_id int) bool { 63 + sql app.db { 64 + delete from Like where user_id == user_id && post_id == post_id 65 + // yeet the old cached like value 66 + delete from LikeCache where post_id == post_id 67 + } or { 68 + return false 69 + } 70 + return true 71 + }
+34
src/database/notification.v
··· 2 3 import entity { Notification } 4 5 // get_notifications_for gets a list of notifications for the given user. 6 pub fn (app &DatabaseAccess) get_notifications_for(user_id int) []Notification { 7 notifications := sql app.db {
··· 2 3 import entity { Notification } 4 5 + // get_notification_by_id gets a notification by its given id, returns none if 6 + // the notification does not exist. 7 + pub fn (app &DatabaseAccess) get_notification_by_id(id int) ?Notification { 8 + notifications := sql app.db { 9 + select from Notification where id == id 10 + } or { [] } 11 + if notifications.len != 1 { 12 + return none 13 + } 14 + return notifications[0] 15 + } 16 + 17 + // delete_notification deletes the given notification, returns true if this 18 + // succeeded and false otherwise. 19 + pub fn (app &DatabaseAccess) delete_notification(id int) bool { 20 + sql app.db { 21 + delete from Notification where id == id 22 + } or { 23 + return false 24 + } 25 + return true 26 + } 27 + 28 + // delete_notifications_for_user deletes all notifications for the given user, 29 + // returns true if this succeeded and false otherwise. 30 + pub fn (app &DatabaseAccess) delete_notifications_for_user(user_id int) bool { 31 + sql app.db { 32 + delete from Notification where user_id == user_id 33 + } or { 34 + return false 35 + } 36 + return true 37 + } 38 + 39 // get_notifications_for gets a list of notifications for the given user. 40 pub fn (app &DatabaseAccess) get_notifications_for(user_id int) []Notification { 41 notifications := sql app.db {
+46
src/database/post.v
··· 3 import time 4 import entity { Post, Like, LikeCache } 5 6 // get_post_by_id gets a post by its id, returns none if it does not exist. 7 pub fn (app &DatabaseAccess) get_post_by_id(id int) ?Post { 8 posts := sql app.db { ··· 84 } or { [] } 85 return posts 86 }
··· 3 import time 4 import entity { Post, Like, LikeCache } 5 6 + // add_post adds a new post to the database, returns true if this succeeded and 7 + // false otherwise. 8 + pub fn (app &DatabaseAccess) add_post(post &Post) bool { 9 + sql app.db { 10 + insert post into Post 11 + } or { 12 + return false 13 + } 14 + return true 15 + } 16 + 17 // get_post_by_id gets a post by its id, returns none if it does not exist. 18 pub fn (app &DatabaseAccess) get_post_by_id(id int) ?Post { 19 posts := sql app.db { ··· 95 } or { [] } 96 return posts 97 } 98 + 99 + // pin_post pins the given post, returns true if this succeeds and false 100 + // otherwise. 101 + pub fn (app &DatabaseAccess) pin_post(post_id int) bool { 102 + sql app.db { 103 + update Post set pinned = true where id == post_id 104 + } or { 105 + return false 106 + } 107 + return true 108 + } 109 + 110 + // update_post updates the given post's title and body with the given title and 111 + // body, returns true if this succeeds and false otherwise. 112 + pub fn (app &DatabaseAccess) update_post(post_id int, new_title string, new_body string) bool { 113 + sql app.db { 114 + update Post set body = new_body, title = new_title where id == post_id 115 + } or { 116 + return false 117 + } 118 + return true 119 + } 120 + 121 + // delete_post deletes the given post and all likes associated with it, returns 122 + // true if this succeeds and false otherwise. 123 + pub fn (app &DatabaseAccess) delete_post(id int) bool { 124 + sql app.db { 125 + delete from Post where id == id 126 + delete from Like where post_id == id 127 + delete from LikeCache where post_id == id 128 + } or { 129 + return false 130 + } 131 + return true 132 + }
+11
src/database/site.v
··· 18 } 19 return configs[0] 20 }
··· 18 } 19 return configs[0] 20 } 21 + 22 + // set_motd sets the site's current message of the day, returns true if this 23 + // succeeds and false otherwise. 24 + pub fn (app &DatabaseAccess) set_motd(motd string) bool { 25 + sql app.db { 26 + update Site set motd = motd where id == 1 27 + } or { 28 + return false 29 + } 30 + return true 31 + }
+35 -1
src/database/user.v
··· 1 module database 2 3 - import entity { User, Notification, Like, Post } 4 5 // new_user creates a new user and returns their struct after creation. 6 pub fn (app &DatabaseAccess) new_user(user User) ?User { ··· 172 } 173 return likes.len == 1 174 }
··· 1 module database 2 3 + import entity { User, Notification, Like, LikeCache, Post } 4 5 // new_user creates a new user and returns their struct after creation. 6 pub fn (app &DatabaseAccess) new_user(user User) ?User { ··· 172 } 173 return likes.len == 1 174 } 175 + 176 + // delete_user deletes the given user and their data, returns true if this 177 + // succeeded and false otherwise. 178 + pub fn (app &DatabaseAccess) delete_user(user_id int) bool { 179 + sql app.db { 180 + delete from User where id == user_id 181 + delete from Like where user_id == user_id 182 + delete from Notification where user_id == user_id 183 + } or { 184 + return false 185 + } 186 + 187 + // delete posts and their likes 188 + posts_from_this_user := sql app.db { 189 + select from Post where author_id == id 190 + } or { [] } 191 + 192 + for post in posts_from_this_user { 193 + sql app.db { 194 + delete from Like where post_id == post.id 195 + delete from LikeCache where post_id == post.id 196 + } or { 197 + eprintln('failed to delete like cache for post during user deletion: ${post.id}') 198 + } 199 + } 200 + 201 + sql app.db { 202 + delete from Post where author_id == id 203 + } or { 204 + eprintln('failed to delete posts by deleting user: ${user_id}') 205 + } 206 + 207 + return true 208 + }
+30 -78
src/webapp/api.v
··· 329 330 @['/api/user/notification/clear'] 331 fn (mut app App) api_user_notification_clear(mut ctx Context, id int) veb.Result { 332 - if !ctx.is_logged_in() { 333 ctx.error('you are not logged in!') 334 return ctx.redirect('/login') 335 } 336 - sql app.db { 337 - delete from Notification where id == id 338 - } or { 339 - ctx.error('failed to delete notification') 340 - return ctx.redirect('/inbox') 341 } 342 return ctx.redirect('/inbox') 343 } 344 ··· 348 ctx.error('you are not logged in!') 349 return ctx.redirect('/login') 350 } 351 - sql app.db { 352 - delete from Notification where user_id == user.id 353 - } or { 354 ctx.error('failed to delete notifications') 355 return ctx.redirect('/inbox') 356 } ··· 368 369 if user.admin || user.id == id { 370 // yeet 371 - sql app.db { 372 - delete from User where id == id 373 - delete from Like where user_id == id 374 - delete from Notification where user_id == id 375 - } or { 376 ctx.error('failed to delete user: ${id}') 377 return ctx.redirect('/') 378 - } 379 - 380 - // delete posts and their likes 381 - posts_from_this_user := sql app.db { 382 - select from Post where author_id == id 383 - } or { [] } 384 - 385 - for post in posts_from_this_user { 386 - sql app.db { 387 - delete from Like where post_id == post.id 388 - delete from LikeCache where post_id == post.id 389 - } or { 390 - eprintln('failed to delete like cache for post during user deletion: ${post.id}') 391 - } 392 - } 393 - 394 - sql app.db { 395 - delete from Post where author_id == id 396 - } or { 397 - eprintln('failed to delete posts by deleting user: ${user.id}') 398 } 399 400 app.auth.delete_tokens_for_user(id) or { ··· 459 post.replying_to = replying_to 460 } 461 462 - sql app.db { 463 - insert post into Post 464 - } or { 465 ctx.error('failed to post!') 466 println('failed to post: ${post} from user ${user.id}') 467 return ctx.redirect('/post/new') ··· 490 } 491 492 if user.admin || user.id == post.author_id { 493 - sql app.db { 494 - delete from Post where id == id 495 - delete from Like where post_id == id 496 - } or { 497 ctx.error('failed to delete post') 498 eprintln('failed to delete post: ${id}') 499 return ctx.redirect('/') ··· 514 post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 515 516 if app.does_user_like_post(user.id, post.id) { 517 - sql app.db { 518 - delete from Like where user_id == user.id && post_id == post.id 519 - // yeet the old cached like value 520 - delete from LikeCache where post_id == post.id 521 - } or { 522 eprintln('user ${user.id} failed to unlike post ${id}') 523 return ctx.server_error('failed to unlike post') 524 } ··· 526 } else { 527 // remove the old dislike, if it exists 528 if app.does_user_dislike_post(user.id, post.id) { 529 - sql app.db { 530 - delete from Like where user_id == user.id && post_id == post.id 531 - } or { 532 eprintln('user ${user.id} failed to remove dislike on post ${id} when liking it') 533 } 534 } ··· 538 post_id: post.id 539 is_like: true 540 } 541 - sql app.db { 542 - insert like into Like 543 - // yeet the old cached like value 544 - delete from LikeCache where post_id == post.id 545 - } or { 546 eprintln('user ${user.id} failed to like post ${id}') 547 return ctx.server_error('failed to like post') 548 } ··· 557 post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 558 559 if app.does_user_dislike_post(user.id, post.id) { 560 - sql app.db { 561 - delete from Like where user_id == user.id && post_id == post.id 562 - // yeet the old cached like value 563 - delete from LikeCache where post_id == post.id 564 - } or { 565 - eprintln('user ${user.id} failed to unlike post ${id}') 566 - return ctx.server_error('failed to unlike post') 567 } 568 return ctx.ok('undisliked post') 569 } else { 570 // remove the old like, if it exists 571 if app.does_user_like_post(user.id, post.id) { 572 - sql app.db { 573 - delete from Like where user_id == user.id && post_id == post.id 574 - } or { 575 eprintln('user ${user.id} failed to remove like on post ${id} when disliking it') 576 } 577 } ··· 581 post_id: post.id 582 is_like: false 583 } 584 - sql app.db { 585 - insert like into Like 586 - // yeet the old cached like value 587 - delete from LikeCache where post_id == post.id 588 - } or { 589 eprintln('user ${user.id} failed to dislike post ${id}') 590 return ctx.server_error('failed to dislike post') 591 } ··· 614 return ctx.redirect('/') 615 } 616 617 - sql app.db { 618 - update Post set body = body, title = title where id == id 619 - } or { 620 eprintln('failed to update post') 621 ctx.error('failed to update post') 622 return ctx.redirect('/') ··· 633 } 634 635 if user.admin { 636 - sql app.db { 637 - update Post set pinned = true where id == id 638 - } or { 639 eprintln('failed to pin post: ${id}') 640 ctx.error('failed to pin post') 641 return ctx.redirect('/post/${id}') ··· 658 } 659 660 if user.admin { 661 - sql app.db { 662 - update Site set motd = motd where id == 1 663 - } or { 664 ctx.error('failed to set motd') 665 eprintln('failed to set motd: ${motd}') 666 return ctx.redirect('/')
··· 329 330 @['/api/user/notification/clear'] 331 fn (mut app App) api_user_notification_clear(mut ctx Context, id int) veb.Result { 332 + user := app.whoami(mut ctx) or { 333 ctx.error('you are not logged in!') 334 return ctx.redirect('/login') 335 } 336 + 337 + if notification := app.get_notification_by_id(id) { 338 + if notification.user_id != user.id { 339 + ctx.error('no such notification for user') 340 + return ctx.redirect('/inbox') 341 + } else { 342 + if !app.delete_notification(id) { 343 + ctx.error('failed to delete notification') 344 + return ctx.redirect('/inbox') 345 + } 346 + } 347 + } else { 348 + ctx.error('no such notification for user') 349 } 350 + 351 return ctx.redirect('/inbox') 352 } 353 ··· 357 ctx.error('you are not logged in!') 358 return ctx.redirect('/login') 359 } 360 + if !app.delete_notifications_for_user(user.id) { 361 ctx.error('failed to delete notifications') 362 return ctx.redirect('/inbox') 363 } ··· 375 376 if user.admin || user.id == id { 377 // yeet 378 + if !app.delete_user(user.id) { 379 ctx.error('failed to delete user: ${id}') 380 return ctx.redirect('/') 381 } 382 383 app.auth.delete_tokens_for_user(id) or { ··· 442 post.replying_to = replying_to 443 } 444 445 + if !app.add_post(post) { 446 ctx.error('failed to post!') 447 println('failed to post: ${post} from user ${user.id}') 448 return ctx.redirect('/post/new') ··· 471 } 472 473 if user.admin || user.id == post.author_id { 474 + if !app.delete_post(post.id) { 475 ctx.error('failed to delete post') 476 eprintln('failed to delete post: ${id}') 477 return ctx.redirect('/') ··· 492 post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 493 494 if app.does_user_like_post(user.id, post.id) { 495 + if !app.unlike_post(post.id, user.id) { 496 eprintln('user ${user.id} failed to unlike post ${id}') 497 return ctx.server_error('failed to unlike post') 498 } ··· 500 } else { 501 // remove the old dislike, if it exists 502 if app.does_user_dislike_post(user.id, post.id) { 503 + if !app.unlike_post(post.id, user.id) { 504 eprintln('user ${user.id} failed to remove dislike on post ${id} when liking it') 505 } 506 } ··· 510 post_id: post.id 511 is_like: true 512 } 513 + if !app.add_like(like) { 514 eprintln('user ${user.id} failed to like post ${id}') 515 return ctx.server_error('failed to like post') 516 } ··· 525 post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 526 527 if app.does_user_dislike_post(user.id, post.id) { 528 + if !app.unlike_post(post.id, user.id) { 529 + eprintln('user ${user.id} failed to undislike post ${id}') 530 + return ctx.server_error('failed to undislike post') 531 } 532 return ctx.ok('undisliked post') 533 } else { 534 // remove the old like, if it exists 535 if app.does_user_like_post(user.id, post.id) { 536 + if !app.unlike_post(post.id, user.id) { 537 eprintln('user ${user.id} failed to remove like on post ${id} when disliking it') 538 } 539 } ··· 543 post_id: post.id 544 is_like: false 545 } 546 + if !app.add_like(like) { 547 eprintln('user ${user.id} failed to dislike post ${id}') 548 return ctx.server_error('failed to dislike post') 549 } ··· 572 return ctx.redirect('/') 573 } 574 575 + if !app.update_post(id, title, body) { 576 eprintln('failed to update post') 577 ctx.error('failed to update post') 578 return ctx.redirect('/') ··· 589 } 590 591 if user.admin { 592 + if !app.pin_post(id) { 593 eprintln('failed to pin post: ${id}') 594 ctx.error('failed to pin post') 595 return ctx.redirect('/post/${id}') ··· 612 } 613 614 if user.admin { 615 + if !app.set_motd(motd) { 616 ctx.error('failed to set motd') 617 eprintln('failed to set motd: ${motd}') 618 return ctx.redirect('/')