a mini social media app for small communities

vfmt

+10 -20
src/api.v
··· 2 2 3 3 import veb 4 4 import auth 5 - import entity { Site, User, Post, Like, LikeCache } 5 + import entity { Like, LikeCache, Post, Site, User } 6 6 7 7 ////// Users ////// 8 8 ··· 27 27 28 28 salt := auth.generate_salt() 29 29 mut user := User{ 30 - username: username 31 - password: auth.hash_password_with_salt(password, salt) 30 + username: username 31 + password: auth.hash_password_with_salt(password, salt) 32 32 password_salt: salt 33 33 } 34 34 ··· 277 277 278 278 @['/api/user/get_name'] 279 279 fn (mut app App) api_user_get_name(mut ctx Context, username string) veb.Result { 280 - user := app.get_user_by_name(username) or { 281 - return ctx.server_error('no such user') 282 - } 280 + user := app.get_user_by_name(username) or { return ctx.server_error('no such user') } 283 281 return ctx.text(user.get_name()) 284 282 } 285 283 ··· 311 309 312 310 post := Post{ 313 311 author_id: user.id 314 - title: title 315 - body: body 312 + title: title 313 + body: body 316 314 } 317 315 318 316 sql app.db { ··· 358 356 359 357 @['/api/post/like'] 360 358 fn (mut app App) api_post_like(mut ctx Context, id int) veb.Result { 361 - user := app.whoami(mut ctx) or { 362 - return ctx.unauthorized('not logged in') 363 - } 359 + user := app.whoami(mut ctx) or { return ctx.unauthorized('not logged in') } 364 360 365 - post := app.get_post_by_id(id) or { 366 - return ctx.server_error('post does not exist') 367 - } 361 + post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 368 362 369 363 if app.does_user_like_post(user.id, post.id) { 370 364 sql app.db { ··· 405 399 406 400 @['/api/post/dislike'] 407 401 fn (mut app App) api_post_dislike(mut ctx Context, id int) veb.Result { 408 - user := app.whoami(mut ctx) or { 409 - return ctx.unauthorized('not logged in') 410 - } 402 + user := app.whoami(mut ctx) or { return ctx.unauthorized('not logged in') } 411 403 412 - post := app.get_post_by_id(id) or { 413 - return ctx.server_error('post does not exist') 414 - } 404 + post := app.get_post_by_id(id) or { return ctx.server_error('post does not exist') } 415 405 416 406 if app.does_user_dislike_post(user.id, post.id) { 417 407 sql app.db {
+12 -14
src/app.v
··· 3 3 import veb 4 4 import db.pg 5 5 import auth 6 - import entity { Site, User, Post, Like, LikeCache } 6 + import entity { LikeCache, Like, Post, Site, User } 7 7 8 8 pub struct App { 9 9 veb.StaticHandler ··· 13 13 db pg.DB 14 14 auth auth.Auth[pg.DB] 15 15 validators struct { 16 - pub: 16 + pub mut: 17 17 username StringValidator 18 18 password StringValidator 19 19 nickname StringValidator ··· 98 98 } 99 99 100 100 pub fn (app &App) whoami(mut ctx Context) ?User { 101 - token := ctx.get_cookie('token') or { 102 - return none 103 - }.trim_space() 101 + token := ctx.get_cookie('token') or { return none }.trim_space() 104 102 if token == '' { 105 103 return none 106 104 } ··· 133 131 } 134 132 135 133 pub fn (app &App) get_unknown_user() User { 136 - return User{ username: 'unknown' } 134 + return User{ 135 + username: 'unknown' 136 + } 137 137 } 138 138 139 139 pub fn (app &App) logged_in_as(mut ctx Context, id int) bool { ··· 204 204 } 205 205 206 206 // cache 207 - cached := LikeCache { 207 + cached := LikeCache{ 208 208 post_id: post_id 209 - likes: likes 209 + likes: likes 210 210 } 211 211 sql app.db { 212 212 insert cached into LikeCache ··· 223 223 224 224 pub fn (app &App) get_or_create_site_config() Site { 225 225 configs := sql app.db { 226 - select from entity.Site 226 + select from Site 227 227 } or { [] } 228 228 if configs.len == 0 { 229 229 // make the site config 230 - site_config := entity.Site{ } 230 + site_config := Site{} 231 231 sql app.db { 232 - insert site_config into entity.Site 233 - } or { 234 - panic('failed to create site config (${err})') 235 - } 232 + insert site_config into Site 233 + } or { panic('failed to create site config (${err})') } 236 234 } else if configs.len > 1 { 237 235 // this should never happen 238 236 panic('there are multiple site configs')
+12 -12
src/config.v
··· 6 6 pub mut: 7 7 dev_mode bool 8 8 static_path string 9 - instance struct { 9 + instance struct { 10 10 pub mut: 11 11 name string 12 12 welcome string 13 13 default_theme string 14 14 allow_changing_theme bool 15 15 } 16 - http struct { 16 + http struct { 17 17 pub mut: 18 18 port int 19 19 } 20 - postgres struct { 20 + postgres struct { 21 21 pub mut: 22 - host string 23 - port int 24 - user string 22 + host string 23 + port int 24 + user string 25 25 password string 26 - db string 26 + db string 27 27 } 28 - post struct { 28 + post struct { 29 29 pub mut: 30 30 title_min_len int 31 31 title_max_len int 32 32 title_pattern string 33 - body_min_len int 34 - body_max_len int 35 - body_pattern string 33 + body_min_len int 34 + body_max_len int 35 + body_pattern string 36 36 } 37 - user struct { 37 + user struct { 38 38 pub mut: 39 39 username_min_len int 40 40 username_max_len int
+3 -3
src/entity/likes.v
··· 12 12 // Stores total likes per post 13 13 pub struct LikeCache { 14 14 pub mut: 15 - id int @[primary; sql: serial] 16 - post_id int 17 - likes int 15 + id int @[primary; sql: serial] 16 + post_id int 17 + likes int 18 18 }
+2 -2
src/entity/post.v
··· 7 7 id int @[primary; sql: serial] 8 8 author_id int 9 9 10 - title string 11 - body string 10 + title string 11 + body string 12 12 13 13 pinned bool 14 14
+1 -1
src/entity/site.v
··· 2 2 3 3 pub struct Site { 4 4 pub mut: 5 - id int @[primary; sql: serial] 5 + id int @[primary; sql: serial] 6 6 motd string 7 7 }
+12 -11
src/main.v
··· 33 33 34 34 mut app := &App{ 35 35 config: config 36 - db: db 37 - auth: auth.new(db) 38 - validators: struct { 39 - username: StringValidator.new(config.user.username_min_len, config.user.username_max_len, config.user.username_pattern) 40 - password: StringValidator.new(config.user.username_min_len, config.user.username_max_len, config.user.username_pattern) 41 - nickname: StringValidator.new(config.user.nickname_min_len, config.user.nickname_max_len, config.user.nickname_pattern) 42 - user_bio: StringValidator.new(config.user.bio_min_len, config.user.bio_max_len, config.user.bio_pattern) 43 - pronouns: StringValidator.new(config.user.pronouns_min_len, config.user.pronouns_max_len, config.user.pronouns_pattern) 44 - post_title: StringValidator.new(config.post.title_min_len, config.post.title_max_len, config.post.title_pattern) 45 - post_body: StringValidator.new(config.post.body_min_len, config.post.body_max_len, config.post.body_pattern) 46 - } 36 + db: db 37 + auth: auth.new(db) 47 38 } 39 + 40 + // vfmt off 41 + app.validators.username = StringValidator.new(config.user.username_min_len, config.user.username_max_len, config.user.username_pattern) 42 + app.validators.password = StringValidator.new(config.user.username_min_len, config.user.username_max_len, config.user.username_pattern) 43 + app.validators.nickname = StringValidator.new(config.user.nickname_min_len, config.user.nickname_max_len, config.user.nickname_pattern) 44 + app.validators.user_bio = StringValidator.new(config.user.bio_min_len, config.user.bio_max_len, config.user.bio_pattern) 45 + app.validators.pronouns = StringValidator.new(config.user.pronouns_min_len, config.user.pronouns_max_len, config.user.pronouns_pattern) 46 + app.validators.post_title = StringValidator.new(config.post.title_min_len, config.post.title_max_len, config.post.title_pattern) 47 + app.validators.post_body = StringValidator.new(config.post.body_min_len, config.post.body_max_len, config.post.body_pattern) 48 + // vfmt on 48 49 49 50 app.mount_static_folder_at(app.config.static_path, '/static')! 50 51 init_db(db)!
+1 -1
src/pages.v
··· 1 1 module main 2 2 3 3 import veb 4 - import entity { User, Post } 4 + import entity { User } 5 5 6 6 fn (mut app App) index(mut ctx Context) veb.Result { 7 7 ctx.title = app.config.instance.name
+2 -1
src/validation.v
··· 12 12 13 13 @[inline] 14 14 pub fn (validator StringValidator) validate(str string) bool { 15 - return str.len > validator.min_len && str.len < validator.max_len && validator.pattern.matches_string(str) 15 + return str.len > validator.min_len && str.len < validator.max_len 16 + && validator.pattern.matches_string(str) 16 17 } 17 18 18 19 pub fn StringValidator.new(min int, max int, pattern string) StringValidator {