diff --git a/src/forum.nim b/src/forum.nim index 24606a1..4dafeca 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -888,20 +888,26 @@ template createTFD() = #[ DB functions. TODO: Move to another module? ]# proc selectUser(userRow: seq[string], avatarSize: int=80): User = - return User( + result = User( name: userRow[0], avatarUrl: userRow[1].getGravatarUrl(avatarSize), lastOnline: userRow[2].parseInt, - rank: parseEnum[Rank](userRow[3]) + rank: parseEnum[Rank](userRow[3]), + isDeleted: userRow[4] == "1" ) + # Don't give data about a deleted user. + if result.isDeleted: + result.name = "DeletedUser" + result.avatarUrl = getGravatarUrl(result.name & userRow[1], avatarSize) + proc selectPost(postRow: seq[string], skippedPosts: seq[int], replyingTo: Option[PostLink], history: seq[PostInfo], likes: seq[User]): Post = return Post( id: postRow[0].parseInt, replyingTo: replyingTo, - author: selectUser(@[postRow[5], postRow[6], postRow[7], postRow[8]]), + author: selectUser(postRow[5..9]), likes: likes, seen: false, # TODO: history: history, @@ -918,6 +924,7 @@ proc selectReplyingTo(replyingTo: string): Option[PostLink] = const replyingToQuery = sql""" select p.id, strftime('%s', p.creation), p.thread, u.name, u.email, strftime('%s', u.lastOnline), u.status, + u.isDeleted, t.name from post p, person u, thread t where p.thread = t.id and p.author = u.id and p.id = ? and p.isDeleted = 0; @@ -931,7 +938,7 @@ proc selectReplyingTo(replyingTo: string): Option[PostLink] = topic: row[^1], threadId: row[2].parseInt(), postId: row[0].parseInt(), - author: some(selectUser(@[row[3], row[4], row[5], row[6]])) + author: some(selectUser(row[3..7])) )) proc selectHistory(postId: int): seq[PostInfo] = @@ -950,7 +957,8 @@ proc selectHistory(postId: int): seq[PostInfo] = proc selectLikes(postId: int): seq[User] = const likeQuery = sql""" - select u.name, u.email, strftime('%s', u.lastOnline), u.status + select u.name, u.email, strftime('%s', u.lastOnline), u.status, + u.isDeleted from like h, person u where h.post = ? and h.author = u.id order by h.creation asc; @@ -963,7 +971,7 @@ proc selectLikes(postId: int): seq[User] = proc selectThreadAuthor(threadId: int): User = const authorQuery = sql""" - select name, email, strftime('%s', lastOnline), status + select name, email, strftime('%s', lastOnline), status, isDeleted from person where id in ( select author from post where thread = ? @@ -981,7 +989,8 @@ proc selectThread(threadRow: seq[string]): Thread = order by creation asc limit 1;""" const usersListQuery = sql""" - select name, email, strftime('%s', lastOnline), status, count(*) + select name, email, strftime('%s', lastOnline), status, u.isDeleted, + count(*) from person u, post p where p.author = u.id and p.thread = ? group by name order by count(*) desc limit 5; """ @@ -1142,7 +1151,7 @@ proc executeLogin(c: TForumData, username, password: string): string = const query = sql""" select id, name, password, email, salt - from person where name = ? or email = ? + from person where (name = ? or email = ?) and isDeleted = 0 """ if username.len == 0: raise newForumError("Username cannot be empty", @["username"]) @@ -1280,6 +1289,8 @@ proc executeDeleteUser(c: TForumData, username: string) = # Set the `isDeleted` flag. exec(db, sql"update person set isDeleted = 1 where name = ?;", username) + logout(c) + proc updateProfile( c: TForumData, username, email: string, rank: Rank ) {.async.} = @@ -1367,7 +1378,8 @@ routes: sql( """select p.id, p.content, strftime('%s', p.creation), p.author, p.replyingTo, - u.name, u.email, strftime('%s', u.lastOnline), u.status + u.name, u.email, strftime('%s', u.lastOnline), u.status, + u.isDeleted from post p, person u where u.id = p.author and p.thread = ? and p.isDeleted = 0 order by p.id""" @@ -1410,7 +1422,8 @@ routes: let postsQuery = sql(""" select p.id, p.content, strftime('%s', p.creation), p.author, p.replyingTo, - u.name, u.email, strftime('%s', u.lastOnline), u.status + u.name, u.email, strftime('%s', u.lastOnline), u.status, + u.isDeleted from post p, person u where u.id = p.author and p.id in ($#) order by p.id; @@ -1477,10 +1490,10 @@ routes: """ % postsFrom) let userQuery = sql(""" - select name, email, strftime('%s', lastOnline), status, + select name, email, strftime('%s', lastOnline), status, isDeleted, strftime('%s', creation), id from person - where name = ? + where name = ? and isDeleted = 0 """) var profile = Profile( @@ -1491,8 +1504,11 @@ routes: let userRow = db.getRow(userQuery, username) let userID = userRow[^1] + if userID.len == 0: + halt() + profile.user = selectUser(userRow, avatarSize=200) - profile.joinTime = userRow[4].parseInt() + profile.joinTime = userRow[^2].parseInt() profile.postCount = getValue(db, sql("select count(*) " & postsFrom), username).parseInt() profile.threadCount = diff --git a/src/frontend/editbox.nim b/src/frontend/editbox.nim index e6c6c62..c05c03d 100644 --- a/src/frontend/editbox.nim +++ b/src/frontend/editbox.nim @@ -59,7 +59,7 @@ when defined(js): proc render*(state: EditBox, post: Post): VNode = if state.status != Http200: - return renderError("Couldn't retrieve raw post") + return renderError("Couldn't retrieve raw post", state.status) if state.rawContent.isNone() or state.post.id != post.id: state.post = post diff --git a/src/frontend/error.nim b/src/frontend/error.nim index 6a68df4..a70708d 100644 --- a/src/frontend/error.nim +++ b/src/frontend/error.nim @@ -1,4 +1,4 @@ -import options +import options, httpcore type PostError* = object errorFields*: seq[string] ## IDs of the fields with an error. @@ -11,7 +11,25 @@ when defined(js): import karaxutils - proc renderError*(message: string): VNode = + proc render404*(): VNode = + result = buildHtml(): + tdiv(class="empty error"): + tdiv(class="empty icon"): + italic(class="fas fa-bug fa-5x") + p(class="empty-title h5"): + text "404 Not Found" + p(class="empty-subtitle"): + text "Cannot find what you are looking for, it might have been " & + "deleted. Sorry!" + tdiv(class="empty-action"): + a(href="/", onClick=anchorCB): + button(class="btn btn-primary"): + text "Go back home" + + proc renderError*(message: string, status: HttpCode): VNode = + if status == Http404: + return render404() + result = buildHtml(): tdiv(class="empty error"): tdiv(class="empty icon"): @@ -61,19 +79,4 @@ when defined(js): state.error = some(PostError( errorFields: @[], message: "Unknown error occurred." - )) - - proc render404*(): VNode = - result = buildHtml(): - tdiv(class="empty error"): - tdiv(class="empty icon"): - italic(class="fas fa-bug fa-5x") - p(class="empty-title h5"): - text "404 Not Found" - p(class="empty-subtitle"): - text "Cannot find what you are looking for, it might have been " & - "deleted. Sorry!" - tdiv(class="empty-action"): - a(href="/", onClick=anchorCB): - button(class="btn btn-primary"): - text "Go back home" \ No newline at end of file + )) \ No newline at end of file diff --git a/src/frontend/forum.nim b/src/frontend/forum.nim index 1ae644e..031590b 100644 --- a/src/frontend/forum.nim +++ b/src/frontend/forum.nim @@ -1,4 +1,4 @@ -import strformat, times, options, json, tables, sugar +import strformat, times, options, json, tables, sugar, httpcore from dom import window, Location include karax/prelude @@ -46,7 +46,7 @@ proc route(routes: openarray[Route]): VNode = if matched: return route.p(params) - return renderError("Unmatched route: " & path) + return renderError("Unmatched route: " & path, Http500) proc render(): VNode = result = buildHtml(tdiv()): diff --git a/src/frontend/postlist.nim b/src/frontend/postlist.nim index 68f2e85..94be2d2 100644 --- a/src/frontend/postlist.nim +++ b/src/frontend/postlist.nim @@ -308,7 +308,7 @@ when defined(js): proc renderPostList*(threadId: int, postId: Option[int], currentUser: Option[User]): VNode = if state.status != Http200: - return renderError("Couldn't retrieve posts.") + return renderError("Couldn't retrieve posts.", state.status) if state.list.isNone or state.list.get().thread.id != threadId: var params = @[("id", $threadId)] diff --git a/src/frontend/profile.nim b/src/frontend/profile.nim index 56e0d43..e26a56d 100644 --- a/src/frontend/profile.nim +++ b/src/frontend/profile.nim @@ -58,7 +58,7 @@ when defined(js): currentUser: Option[User] ): VNode = if state.status != Http200: - return renderError("Couldn't retrieve profile.") + return renderError("Couldn't retrieve profile.", state.status) if state.profile.isNone or state.profile.get().user.name != username: let uri = makeUri("profile.json", ("username", username)) diff --git a/src/frontend/threadlist.nim b/src/frontend/threadlist.nim index 68391ff..e8b252b 100644 --- a/src/frontend/threadlist.nim +++ b/src/frontend/threadlist.nim @@ -161,7 +161,7 @@ when defined(js): proc genThreadList(currentUser: Option[User]): VNode = if state.status != Http200: - return renderError("Couldn't retrieve threads.") + return renderError("Couldn't retrieve threads.", state.status) if state.list.isNone: if not state.loading: diff --git a/src/frontend/user.nim b/src/frontend/user.nim index 4b2d212..c43288d 100644 --- a/src/frontend/user.nim +++ b/src/frontend/user.nim @@ -20,6 +20,7 @@ type avatarUrl*: string lastOnline*: int64 rank*: Rank + isDeleted*: bool proc isOnline*(user: User): bool = return getTime().toUnix() - user.lastOnline < (60*5)