From f8781ba5f35632194f3542ee3e2bc7f6a6c9e441 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 10 Aug 2018 16:51:22 +0900 Subject: [PATCH 1/9] Add better RST preview error (#196) * Add better RST preview error * Add exception information back --- src/forum.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/forum.nim b/src/forum.nim index 399a64e..da38305 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -1071,7 +1071,8 @@ routes: except EParseError: let err = PostError( errorFields: @[], - message: getCurrentExceptionMsg() + message: "Message needs to be valid RST! Error: " & + getCurrentExceptionMsg() ) resp Http400, $(%err), "application/json" From 7321ee6f6125bac0c8091baeda2ef572e0820ff7 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 16 Aug 2018 11:10:49 +0900 Subject: [PATCH 2/9] Add case insensitive check to username validation --- src/forum.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forum.nim b/src/forum.nim index da38305..ad1ab09 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -608,7 +608,7 @@ proc executeRegister(c: TForumData, name, pass, antibot, userIp, raise newForumError("Invalid username", @["username"]) if getValue( db, - sql"select name from person where name = ? and isDeleted = 0", + sql"select name from person where name = ? collate nocase and isDeleted = 0", name ).len > 0: raise newForumError("Username already exists", @["username"]) From 2b88a54f5475174b1b8c38634dcd08c99e2fa7b2 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 16 Aug 2018 11:29:57 +0900 Subject: [PATCH 3/9] Add test for case insensitive name check --- tests/browsertests/common.nim | 14 ++++++++------ tests/browsertests/scenario1.nim | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/browsertests/common.nim b/tests/browsertests/common.nim index 2c60840..a2c9b51 100644 --- a/tests/browsertests/common.nim +++ b/tests/browsertests/common.nim @@ -115,7 +115,7 @@ proc login*(session: Session, user, password: string) = checkText "#profile-btn #profile-name", user click "#profile-btn" -proc register*(session: Session, user, password: string) = +proc register*(session: Session, user, password: string, verify = true) = with session: click "#signup-btn" @@ -130,11 +130,13 @@ proc register*(session: Session, user, password: string) = click "#signup-modal .create-account-btn" wait() - # Verify that the user menu has been initialised properly. - click "#profile-btn" - checkText "#profile-btn #profile-name", user - # close menu - click "#profile-btn" + if verify: + with session: + # Verify that the user menu has been initialised properly. + click "#profile-btn" + checkText "#profile-btn #profile-name", user + # close menu + click "#profile-btn" proc createThread*(session: Session, title, content: string) = with session: diff --git a/tests/browsertests/scenario1.nim b/tests/browsertests/scenario1.nim index 34132e5..b5912ec 100644 --- a/tests/browsertests/scenario1.nim +++ b/tests/browsertests/scenario1.nim @@ -30,5 +30,18 @@ proc test*(session: Session, baseUrl: string) = test "can register": with session: register("test", "test") + logout() - session.logout() + test "can't register same username with different case": + with session: + register "test1", "test1", verify = false + logout() + + navigate baseUrl + wait() + + register "TEst1", "test1", verify = false + + ensureExists "#signup-form .has-error" + navigate baseUrl + wait() \ No newline at end of file From 30dc09f453235bbb2f8509f60c6a57f80b402294 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 21 Aug 2018 23:59:42 +0200 Subject: [PATCH 4/9] Hide all the avatars but the first on small screens --- public/css/nimforum.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/css/nimforum.scss b/public/css/nimforum.scss index e970e8e..530180b 100644 --- a/public/css/nimforum.scss +++ b/public/css/nimforum.scss @@ -253,6 +253,13 @@ $threads-meta-color: #545d70; } +// Hide all the avatars but the first on small screens. +@media screen and (max-width: 600px) { + #threads-list a:not(:first-child) > .avatar { + display: none; + } +} + .posts, .about { @extend .grid-md; @extend .container; From f4af965a2e0c146c8d5e33e203cde3dd1c5e22b0 Mon Sep 17 00:00:00 2001 From: lallulli Date: Thu, 31 Oct 2019 11:36:05 +0100 Subject: [PATCH 5/9] Better way of updating nginx configuration Using `nginx -s reload` is better than restarting the server on live systems, because if there is any problem with new config files, nginx will warn you, refuse to reload and continue to work with old configuration. Moreover, this command will minimize downtime. --- setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.md b/setup.md index ea3dc1b..d0f2d3e 100644 --- a/setup.md +++ b/setup.md @@ -100,7 +100,7 @@ You should then create a symlink to this file inside ``/etc/nginx/sites-enabled/ ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/ ``` -Then restart nginx by running ``sudo systemctl restart nginx``. +Then reload nginx configuration by running ``sudo nginx -s reload``. ### Supervisor @@ -168,4 +168,4 @@ You should see something like this: ## Conclusion That should be all you need to get started. Your forum should now be accessible -via your hostname, assuming that it points to your VPS' IP address. \ No newline at end of file +via your hostname, assuming that it points to your VPS' IP address. From f93bd87316bd12fa3bf8091f5ca4bb3bd64f939d Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Feb 2020 06:41:09 -0700 Subject: [PATCH 6/9] Update for Nim 1.0.6 --- nimforum.nimble | 12 ++++++------ src/auth.nim | 8 ++++---- src/forum.nim | 11 +++++------ src/frontend/error.nim | 4 ++-- src/frontend/forum.nim | 2 +- src/frontend/karaxutils.nim | 10 ++-------- src/frontend/login.nim | 4 ++-- src/frontend/replybox.nim | 4 ++-- src/frontend/resetpassword.nim | 4 ++-- src/frontend/threadlist.nim | 2 +- src/utils.nim | 9 ++------- tests/browsertester.nim | 5 ++++- 12 files changed, 33 insertions(+), 42 deletions(-) diff --git a/nimforum.nimble b/nimforum.nimble index 1ea21bd..d6c08f8 100644 --- a/nimforum.nimble +++ b/nimforum.nimble @@ -12,16 +12,16 @@ skipExt = @["nim"] # Dependencies -requires "nim >= 0.18.1" -requires "jester 0.4.0" +requires "nim >= 1.0.6" +requires "jester#d8a03aa" requires "bcrypt#head" requires "hmac#9c61ebe2fd134cf97" -requires "recaptcha 1.0.2" +requires "recaptcha#d06488e" requires "sass#649e0701fa5c" -requires "karax#d8df257dd" +requires "karax#f6bda9a" -requires "webdriver#20f3c1b" +requires "webdriver#c2fee57" # Tasks @@ -36,7 +36,7 @@ task frontend, "Builds the necessary JS frontend (with CSS)": exec "nimble c -r src/buildcss" exec "nimble js -d:release src/frontend/forum.nim" mkDir "public/js" - cpFile "src/frontend/nimcache/forum.js", "public/js/forum.js" + cpFile "src/frontend/forum.js", "public/js/forum.js" task minify, "Minifies the JS using Google's closure compiler": exec "closure-compiler public/js/forum.js --js_output_file public/js/forum.js.opt" diff --git a/src/auth.nim b/src/auth.nim index 0b08bfe..381b666 100644 --- a/src/auth.nim +++ b/src/auth.nim @@ -71,13 +71,13 @@ when isMainModule: "test", "$2a$08$bY85AhoD1e9u0IsD9sM7Ee6kFSLeXRLxJ6rMgfb1wDnU9liaymoTG", 1526908753, - "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\um" + "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\\um" ) let ident2 = makeIdentHash( "test", "$2a$08$bY85AhoD1e9u0IsD9sM7Ee6kFSLeXRLxJ6rMgfb1wDnU9liaymoTG", 1526908753, - "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\um" + "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\\um" ) doAssert ident == ident2 @@ -85,6 +85,6 @@ when isMainModule: "test", "$2a$08$bY85AhoD1e9u0IsD9sM7Ee6kFSLeXRLxJ6rMgfb1wDnU9liaymoTG", 1526908754, - "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\um" + "*B2a] IL\"~sh)q-GBd/i$^>.TL]PR~>1IX>Fp-:M3pCm^cFD\\um" ) - doAssert ident != invalid \ No newline at end of file + doAssert ident != invalid diff --git a/src/forum.nim b/src/forum.nim index ad1ab09..10d3511 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -8,7 +8,7 @@ import system except Thread import os, strutils, times, md5, strtabs, math, db_sqlite, - scgi, jester, asyncdispatch, asyncnet, sequtils, + jester, asyncdispatch, asyncnet, sequtils, parseutils, random, rst, recaptcha, json, re, sugar, strformat, logging import cgi except setCookie @@ -76,7 +76,6 @@ proc getGravatarUrl(email: string, size = 80): string = # ----------------------------------------------------------------------------- -template `||`(x: untyped): untyped = (if not isNil(x): x else: "") proc validateCaptcha(recaptchaResp, ip: string) {.async.} = # captcha validation: @@ -133,9 +132,9 @@ proc checkLoggedIn(c: TForumData) = let row = getRow(db, sql"select name, email, status from person where id = ?", c.userid) - c.username = ||row[0] - c.email = ||row[1] - c.rank = parseEnum[Rank](||row[2]) + c.username = row[0] + c.email = row[1] + c.rank = parseEnum[Rank](row[2]) # In order to handle the "last visit" line appropriately, i.e. # it shouldn't disappear after a refresh, we need to manage a @@ -463,7 +462,7 @@ proc executeReply(c: TForumData, threadId: int, content: string, crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"), c.userId, c.req.ip, content, $threadId, if replyingTo.isSome(): $replyingTo.get() - else: nil + else: "-1" ) discard tryExec( db, diff --git a/src/frontend/error.nim b/src/frontend/error.nim index 4a23c44..06a8d07 100644 --- a/src/frontend/error.nim +++ b/src/frontend/error.nim @@ -86,8 +86,8 @@ when defined(js): state.error = some(error) except: - kout(getCurrentExceptionMsg().cstring) + echo getCurrentExceptionMsg() state.error = some(PostError( errorFields: @[], message: "Unknown error occurred." - )) \ No newline at end of file + )) diff --git a/src/frontend/forum.nim b/src/frontend/forum.nim index 4dea047..5fb11b4 100644 --- a/src/frontend/forum.nim +++ b/src/frontend/forum.nim @@ -49,7 +49,7 @@ proc onPopState(event: dom.Event) = # This event is usually only called when the user moves back in their # history. I fire it in karaxutils.anchorCB as well to ensure the URL is # always updated. This should be moved into Karax in the future. - kout(kstring"New URL: ", window.location.href, " ", state.url.href) + echo "New URL: ", window.location.href, " ", state.url.href document.title = state.originalTitle if state.url.href != window.location.href: state = newState() # Reload the state to remove stale data. diff --git a/src/frontend/karaxutils.nim b/src/frontend/karaxutils.nim index 462d000..2aa9d39 100644 --- a/src/frontend/karaxutils.nim +++ b/src/frontend/karaxutils.nim @@ -25,7 +25,7 @@ proc getInt64*(s: string, default = 0): int64 = when defined(js): include karax/prelude - import karax / [kdom] + import karax / [kdom, kajax] from dom import nil @@ -87,16 +87,10 @@ when defined(js): navigateTo(url) - type - FormData* = ref object - proc newFormData*(): FormData - {.importcpp: "new FormData()", constructor.} proc newFormData*(form: dom.Element): FormData {.importcpp: "new FormData(@)", constructor.} proc get*(form: FormData, key: cstring): cstring {.importcpp: "#.get(@)".} - proc append*(form: FormData, key, val: cstring) - {.importcpp: "#.append(@)".} proc renderProfileUrl*(username: string): string = makeUri(fmt"/profile/{username}") @@ -120,4 +114,4 @@ when defined(js): inc(i) # Skip = i += query.parseUntil(val, '&', i) inc(i) # Skip & - result[$decodeUri(key)] = $decodeUri(val) \ No newline at end of file + result[$decodeUri(key)] = $decodeUri(val) diff --git a/src/frontend/login.nim b/src/frontend/login.nim index f0779c7..c19088e 100644 --- a/src/frontend/login.nim +++ b/src/frontend/login.nim @@ -1,6 +1,6 @@ when defined(js): import sugar, httpcore, options, json - import dom except Event + import dom except Event, KeyboardEvent include karax/prelude import karax / [kajax, kdom] @@ -93,4 +93,4 @@ when defined(js): (state.onSignUp(); state.shown = false)): text "Create account" - render(state.resetPasswordModal, recaptchaSiteKey) \ No newline at end of file + render(state.resetPasswordModal, recaptchaSiteKey) diff --git a/src/frontend/replybox.nim b/src/frontend/replybox.nim index 9d815f6..e386dcb 100644 --- a/src/frontend/replybox.nim +++ b/src/frontend/replybox.nim @@ -26,7 +26,7 @@ when defined(js): proc performScroll() = let replyBox = dom.document.getElementById("reply-box") - replyBox.scrollIntoView(false) + replyBox.scrollIntoView() proc show*(state: ReplyBox) = # Scroll to the reply box. @@ -44,7 +44,7 @@ when defined(js): proc onPreviewPost(httpStatus: int, response: kstring, state: ReplyBox) = postFinished: - kout(response) + echo response state.rendering = some[kstring](response) proc onPreviewClick(e: Event, n: VNode, state: ReplyBox) = diff --git a/src/frontend/resetpassword.nim b/src/frontend/resetpassword.nim index c2f01a1..51b467b 100644 --- a/src/frontend/resetpassword.nim +++ b/src/frontend/resetpassword.nim @@ -1,6 +1,6 @@ when defined(js): import sugar, httpcore, options, json - import dom except Event + import dom except Event, KeyboardEvent include karax/prelude import karax / [kajax, kdom] @@ -152,4 +152,4 @@ when defined(js): ), `type`="button", onClick=(ev: Event, n: VNode) => onClick(ev, n, state)): - text "Reset password" \ No newline at end of file + text "Reset password" diff --git a/src/frontend/threadlist.nim b/src/frontend/threadlist.nim index 0516d74..b3b3d81 100644 --- a/src/frontend/threadlist.nim +++ b/src/frontend/threadlist.nim @@ -60,7 +60,7 @@ when defined(js): if user.isNone(): return not thread.isModerated let rank = user.get().rank - if rank < Moderator and thread.isModerated: + if rank < Rank.Moderator and thread.isModerated: return thread.author == user.get() return true diff --git a/src/utils.nim b/src/utils.nim index 2676ab8..1be058b 100644 --- a/src/utils.nim +++ b/src/utils.nim @@ -10,11 +10,6 @@ let import frontend/[karaxutils, error] export parseInt -proc `%`*[T](opt: Option[T]): JsonNode = - ## Generic constructor for JSON data. Creates a new ``JNull JsonNode`` - ## if ``opt`` is empty, otherwise it delegates to the underlying value. - if opt.isSome: %opt.get else: newJNull() - type Config* = object smtpAddress*: string @@ -56,7 +51,7 @@ proc loadConfig*(filename = getCurrentDir() / "forum.json"): Config = smtpPassword: "", mlistAddress: "") let root = parseFile(filename) result.smtpAddress = root{"smtpAddress"}.getStr("") - result.smtpPort = root{"smtpPort"}.getNum(25).int + result.smtpPort = root{"smtpPort"}.getInt(25) result.smtpUser = root{"smtpUser"}.getStr("") result.smtpPassword = root{"smtpPassword"}.getStr("") result.smtpFromAddr = root{"smtpFromAddr"}.getStr("") @@ -69,7 +64,7 @@ proc loadConfig*(filename = getCurrentDir() / "forum.json"): Config = result.name = root["name"].getStr() result.title = root["title"].getStr() result.ga = root{"ga"}.getStr() - result.port = root{"port"}.getNum(5000).int + result.port = root{"port"}.getInt(5000) proc processGT(n: XmlNode, tag: string): (int, XmlNode, string) = result = (0, newElement(tag), tag) diff --git a/tests/browsertester.nim b/tests/browsertester.nim index 0f4efe9..4e03081 100644 --- a/tests/browsertester.nim +++ b/tests/browsertester.nim @@ -45,7 +45,7 @@ template withBackend(body: untyped): untyped = import browsertests/[scenario1, threads, issue181] -when isMainModule: +proc main() = spawn runProcess("geckodriver -p 4444 --log config") defer: discard execCmd("killall geckodriver") @@ -70,3 +70,6 @@ when isMainModule: except: sleep(10000) # See if we can grab any more output. raise + +when isMainModule: + main() From 64262978dbe427b218f6ba615f9e2e9959d47b69 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Feb 2020 07:39:55 -0700 Subject: [PATCH 7/9] Fix travis --- .travis.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b6177c4..b5c6c9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: - "$HOME/.choosenim" addons: - firefox: "60.0.1" + firefox: "73.0" before_install: - sudo apt-get -qq update @@ -26,13 +26,13 @@ before_install: - sudo make -j5 install - cd .. - - wget https://github.com/mozilla/geckodriver/releases/download/v0.20.1/geckodriver-v0.20.1-linux64.tar.gz + - wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz - mkdir geckodriver - - tar -xzf geckodriver-v0.20.1-linux64.tar.gz -C geckodriver + - tar -xzf geckodriver-v0.26.0-linux64.tar.gz -C geckodriver - export PATH=$PATH:$PWD/geckodriver install: - - export CHOOSENIM_CHOOSE_VERSION="#f92d61b1f4e193bd" + - export CHOOSENIM_CHOOSE_VERSION="stable" - | curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y @@ -41,4 +41,5 @@ install: script: - export MOZ_HEADLESS=1 - - nimble -y test \ No newline at end of file + - nimble -y install + - nimble -y test From 14a0864d867c16bc6d6ac1e5d9877795992e9cb5 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Feb 2020 08:42:27 -0700 Subject: [PATCH 8/9] Version bump --- nimforum.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimforum.nimble b/nimforum.nimble index d6c08f8..97581b0 100644 --- a/nimforum.nimble +++ b/nimforum.nimble @@ -1,5 +1,5 @@ # Package -version = "2.0.1" +version = "2.0.2" author = "Dominik Picheta" description = "The Nim forum" license = "MIT" From 2717496bb5f2841243e5b92003a375cd681186bd Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 14 Feb 2020 09:27:05 -0700 Subject: [PATCH 9/9] Handle no replyingTo option better --- src/forum.nim | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/forum.nim b/src/forum.nim index 10d3511..9ca8abf 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -457,13 +457,21 @@ proc executeReply(c: TForumData, threadId: int, content: string, if isLocked == "1": raise newForumError("Cannot reply to a locked thread.") - let retID = insertID( - db, - crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"), - c.userId, c.req.ip, content, $threadId, - if replyingTo.isSome(): $replyingTo.get() - else: "-1" - ) + var retID: int64 + + if replyingTo.isSome(): + retID = insertID( + db, + crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"), + c.userId, c.req.ip, content, $threadId, $replyingTo.get() + ) + else: + retID = insertID( + db, + crud(crCreate, "post", "author", "ip", "content", "thread"), + c.userId, c.req.ip, content, $threadId + ) + discard tryExec( db, crud(crCreate, "post_fts", "id", "content"),