Fixes forum to work in dev mode. Creates setup_nimforum script.
This commit is contained in:
parent
3804faab3b
commit
7eb6b081ad
7 changed files with 259 additions and 216 deletions
128
createdb.nim
128
createdb.nim
|
|
@ -1,128 +0,0 @@
|
|||
#
|
||||
#
|
||||
# The Nim Forum
|
||||
# (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
|
||||
# Look at license.txt for more info.
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
import strutils, db_sqlite
|
||||
|
||||
var db = open(connection="nimforum.db", user="postgres", password="",
|
||||
database="nimforum")
|
||||
|
||||
const
|
||||
TUserName = "varchar(20)"
|
||||
TPassword = "varchar(32)"
|
||||
TEmail = "varchar(30)"
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists thread(
|
||||
id integer primary key,
|
||||
name varchar(100) not null,
|
||||
views integer not null,
|
||||
modified timestamp not null default (DATETIME('now'))
|
||||
);""", [])
|
||||
|
||||
db.exec(sql"""
|
||||
create unique index if not exists ThreadNameIx on thread (name);
|
||||
""", [])
|
||||
|
||||
db.exec(sql("""
|
||||
create table if not exists person(
|
||||
id integer primary key,
|
||||
name $# not null,
|
||||
password $# not null,
|
||||
email $# not null,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
salt varbin(128) not null,
|
||||
status varchar(30) not null,
|
||||
lastOnline timestamp not null default (DATETIME('now'))
|
||||
);""" % [TUserName, TPassword, TEmail]), [])
|
||||
# echo "person table already exists"
|
||||
|
||||
db.exec(sql("""
|
||||
alter table person
|
||||
add ban varchar(128) not null default ''
|
||||
"""))
|
||||
|
||||
db.exec(sql"""
|
||||
create unique index if not exists UserNameIx on person (name);
|
||||
""", [])
|
||||
|
||||
# ----------------------- Forum ------------------------------------------------
|
||||
|
||||
|
||||
if not db.tryExec(sql"""
|
||||
create table if not exists post(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
ip inet not null,
|
||||
header varchar(100) not null,
|
||||
content varchar(1000) not null,
|
||||
thread integer not null,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (thread) references thread(id),
|
||||
foreign key (author) references person(id)
|
||||
);""", []):
|
||||
echo "post table already exists"
|
||||
|
||||
# -------------------- Session -------------------------------------------------
|
||||
|
||||
if not db.tryExec(sql("""
|
||||
create table if not exists session(
|
||||
id integer primary key,
|
||||
ip inet not null,
|
||||
password $# not null,
|
||||
userid integer not null,
|
||||
lastModified timestamp not null default (DATETIME('now')),
|
||||
foreign key (userid) references person(id)
|
||||
);""" % [TPassword]), []):
|
||||
echo "session table already exists"
|
||||
|
||||
if not db.tryExec(sql"""
|
||||
create table if not exists antibot(
|
||||
id integer primary key,
|
||||
ip inet not null,
|
||||
answer varchar(30) not null,
|
||||
created timestamp not null default (DATETIME('now'))
|
||||
);""", []):
|
||||
echo "antibot table already exists"
|
||||
|
||||
|
||||
db.exec sql"create index PersonStatusIdx on person(status);"
|
||||
db.exec sql"create index PostByAuthorIdx on post(thread, author);"
|
||||
|
||||
# -------------------- Search --------------------------------------------------
|
||||
|
||||
if not db.tryExec(sql"""
|
||||
CREATE VIRTUAL TABLE thread_fts USING fts4 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL
|
||||
);""", []):
|
||||
echo "thread_fts table already exists or fts4 not supported"
|
||||
else:
|
||||
db.exec(sql"""
|
||||
INSERT INTO thread_fts
|
||||
SELECT id, name FROM thread;
|
||||
""", [])
|
||||
if not db.tryExec(sql"""
|
||||
CREATE VIRTUAL TABLE post_fts USING fts4 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
header VARCHAR(100) NOT NULL,
|
||||
content VARCHAR(1000) NOT NULL
|
||||
);""", []):
|
||||
echo "post_fts table already exists or fts4 not supported"
|
||||
else:
|
||||
db.exec(sql"""
|
||||
INSERT INTO post_fts
|
||||
SELECT id, header, content FROM post;
|
||||
""", [])
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
#discard stdin.readline()
|
||||
|
||||
close(db)
|
||||
|
|
@ -320,12 +320,9 @@
|
|||
<td>${fieldValid(c, "email", "E-Mail:")}</td>
|
||||
<td>${textWidget(c, "email", reuseText, maxlength=300)}</td>
|
||||
</tr>
|
||||
#if useCaptcha:
|
||||
<tr>
|
||||
<td>${fieldValid(c, "g-recaptcha-response", "Captcha:")}</td>
|
||||
<td>${captcha.render(includeNoScript=true)}</td>
|
||||
</tr>
|
||||
#end if
|
||||
</table>
|
||||
#if c.errorMsg != "":
|
||||
<div style="float: left; width: 100%;">
|
||||
|
|
@ -494,12 +491,10 @@
|
|||
<td>${fieldValid(c, "nick", "Your nickname:")}</td>
|
||||
<td><input type="text" name="nick" maxlength="20" /></td>
|
||||
</tr>
|
||||
#if useCaptcha:
|
||||
<tr>
|
||||
<td>${fieldValid(c, "g-recaptcha-response", "Captcha:")}</td>
|
||||
<td>${captcha.render(includeNoScript=true)}</td>
|
||||
</tr>
|
||||
#end if
|
||||
</table>
|
||||
#if c.errorMsg != "":
|
||||
<div style="float: left; width: 100%;">
|
||||
|
|
|
|||
98
forum.nim
98
forum.nim
|
|
@ -13,6 +13,8 @@ import
|
|||
import cgi except setCookie
|
||||
import options
|
||||
|
||||
import auth
|
||||
|
||||
import frontend/threadlist except User
|
||||
import frontend/[
|
||||
category, postlist, error, header, post, profile, user, karaxutils
|
||||
|
|
@ -85,7 +87,6 @@ var
|
|||
db: DbConn
|
||||
isFTSAvailable: bool
|
||||
config: Config
|
||||
useCaptcha: bool
|
||||
captcha: ReCaptcha
|
||||
|
||||
proc newForumError(message: string,
|
||||
|
|
@ -230,62 +231,7 @@ proc genGravatar(email: string, size: int = 80): string =
|
|||
result = "<img width=\"$1\" height=\"$2\" src=\"$3\" />" %
|
||||
[$size, $size, getGravatarUrl(email, size)]
|
||||
|
||||
proc randomSalt(): string =
|
||||
result = ""
|
||||
for i in 0..127:
|
||||
var r = random(225)
|
||||
if r >= 32 and r <= 126:
|
||||
result.add(chr(random(225)))
|
||||
|
||||
proc devRandomSalt(): string =
|
||||
when defined(posix):
|
||||
result = ""
|
||||
var f = open("/dev/urandom")
|
||||
var randomBytes: array[0..127, char]
|
||||
discard f.readBuffer(addr(randomBytes), 128)
|
||||
for i in 0..127:
|
||||
if ord(randomBytes[i]) >= 32 and ord(randomBytes[i]) <= 126:
|
||||
result.add(randomBytes[i])
|
||||
f.close()
|
||||
else:
|
||||
result = randomSalt()
|
||||
|
||||
proc makeSalt(): string =
|
||||
## Creates a salt using a cryptographically secure random number generator.
|
||||
##
|
||||
## Ensures that the resulting salt contains no ``\0``.
|
||||
try:
|
||||
result = devRandomSalt()
|
||||
except IOError:
|
||||
result = randomSalt()
|
||||
|
||||
var newResult = ""
|
||||
for i in 0 .. <result.len:
|
||||
if result[i] != '\0':
|
||||
newResult.add result[i]
|
||||
return newResult
|
||||
|
||||
proc makePassword(password, salt: string, comparingTo = ""): string =
|
||||
## Creates an MD5 hash by combining password and salt.
|
||||
when defined(windows):
|
||||
result = getMD5(salt & getMD5(password))
|
||||
else:
|
||||
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
|
||||
result = hash(getMD5(salt & getMD5(password)), bcryptSalt)
|
||||
|
||||
proc makeIdentHash(user, password, epoch, secret: string,
|
||||
comparingTo = ""): string =
|
||||
## Creates a hash verifying the identity of a user. Used for password reset
|
||||
## links and email activation links.
|
||||
## If ``epoch`` is smaller than the epoch of the user's last login then
|
||||
## the link is invalid.
|
||||
## The ``secret`` is the 'salt' field in the ``person`` table.
|
||||
echo(user, password, epoch, secret)
|
||||
when defined(windows):
|
||||
result = getMD5(user & password & epoch & secret)
|
||||
else:
|
||||
let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8)
|
||||
result = hash(user & password & epoch & secret, bcryptSalt)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
template `||`(x: untyped): untyped = (if not isNil(x): x else: "")
|
||||
|
|
@ -301,7 +247,7 @@ proc setError(c: TForumData, field, msg: string): bool {.inline.} =
|
|||
|
||||
proc resetPassword(c: TForumData, nick, antibot, userIp: string): Future[bool] {.async.} =
|
||||
# captcha validation:
|
||||
if useCaptcha:
|
||||
if config.recaptchaSecretKey.len > 0:
|
||||
var captchaValid: bool = false
|
||||
try:
|
||||
captchaValid = await captcha.verify(antibot, userIp)
|
||||
|
|
@ -365,15 +311,10 @@ proc checkLoggedIn(c: TForumData) =
|
|||
c.req.ip, pass)
|
||||
|
||||
let row = getRow(db,
|
||||
sql"select name, email, status, ban from person where id = ?", c.userid)
|
||||
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])
|
||||
let ban = getBanErrorMsg(||row[3], c.rank)
|
||||
if ban.len > 0:
|
||||
discard c.setError("name", ban)
|
||||
logout(c)
|
||||
return
|
||||
|
||||
# Update lastOnline
|
||||
db.exec(sql"update person set lastOnline = DATETIME('now') where id = ?",
|
||||
|
|
@ -917,16 +858,13 @@ proc initialise() =
|
|||
database="nimforum")
|
||||
isFTSAvailable = db.getAllRows(sql("SELECT name FROM sqlite_master WHERE " &
|
||||
"type='table' AND name='post_fts'")).len == 1
|
||||
|
||||
config = loadConfig()
|
||||
if len(config.recaptchaSecretKey) > 0 and len(config.recaptchaSiteKey) > 0:
|
||||
useCaptcha = true
|
||||
captcha = initReCaptcha(config.recaptchaSecretKey, config.recaptchaSiteKey)
|
||||
else:
|
||||
useCaptcha = false
|
||||
var http = true
|
||||
if paramCount() > 0:
|
||||
if paramStr(1) == "scgi":
|
||||
http = false
|
||||
doAssert config.isDev, "Recaptcha required for production!"
|
||||
echo("[WARNING] No recaptcha secret key specified.")
|
||||
|
||||
template createTFD() =
|
||||
var c {.inject.}: TForumData
|
||||
|
|
@ -1027,13 +965,13 @@ proc executeReply(c: TForumData, threadId: int, content: string,
|
|||
# Verify that content can be parsed as RST.
|
||||
let retID = insertID(
|
||||
db,
|
||||
crud(crCreate, "post", "author", "ip", "header", "content", "thread"),
|
||||
c.userId, c.req.ip, subject, content, $threadId, ""
|
||||
crud(crCreate, "post", "author", "ip", "content", "thread"),
|
||||
c.userId, c.req.ip, content, $threadId, ""
|
||||
)
|
||||
discard tryExec(
|
||||
db,
|
||||
crud(crCreate, "post_fts", "id", "header", "content"),
|
||||
retID.int, subject, content
|
||||
crud(crCreate, "post_fts", "id", "content"),
|
||||
retID.int, content
|
||||
)
|
||||
|
||||
exec(db, sql"update thread set modified = DATETIME('now') where id = ?",
|
||||
|
|
@ -1156,7 +1094,7 @@ proc executeRegister(c: TForumData, name, pass, antibot, userIp,
|
|||
raise newForumError("Please choose a longer password", @["password"])
|
||||
|
||||
# captcha validation:
|
||||
if useCaptcha:
|
||||
if config.recaptchaSecretKey.len > 0:
|
||||
var verifyFut = captcha.verify(antibot, userIp)
|
||||
yield verifyFut
|
||||
if verifyFut.failed:
|
||||
|
|
@ -1409,14 +1347,22 @@ routes:
|
|||
post "/signup":
|
||||
createTFD()
|
||||
let formData = request.formData
|
||||
if not config.isDev:
|
||||
cond "g-recaptcha-response" in formData
|
||||
|
||||
let username = formData["username"].body
|
||||
let password = formData["password"].body
|
||||
let recaptcha =
|
||||
if "g-recaptcha-response" in formData:
|
||||
formData["g-recaptcha-response"].body
|
||||
else:
|
||||
""
|
||||
try:
|
||||
discard await executeRegister(
|
||||
c,
|
||||
username,
|
||||
password,
|
||||
formData["g-recaptcha-response"].body,
|
||||
recaptcha,
|
||||
request.host,
|
||||
formData["email"].body
|
||||
)
|
||||
|
|
@ -1446,7 +1392,7 @@ routes:
|
|||
let status = UserStatus(
|
||||
user: user,
|
||||
recaptchaSiteKey:
|
||||
if useCaptcha:
|
||||
if not config.isDev:
|
||||
some(config.recaptchaSiteKey)
|
||||
else:
|
||||
none[string]()
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ when defined(js):
|
|||
not getLoggedInUser().isNone
|
||||
|
||||
proc renderHeader*(): VNode =
|
||||
if state.data.isNone:
|
||||
getStatus() # TODO: Call this every render?
|
||||
if state.data.isNone and state.status == Http200:
|
||||
getStatus()
|
||||
|
||||
let user = state.data.map(x => x.user).flatten
|
||||
result = buildHtml(tdiv()): # TODO: Why do some buildHtml's need this?
|
||||
|
|
|
|||
228
setup_nimforum.nim
Normal file
228
setup_nimforum.nim
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#
|
||||
#
|
||||
# The Nim Forum
|
||||
# (c) Copyright 2018 Andreas Rumpf, Dominik Picheta
|
||||
# Look at license.txt for more info.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Script to initialise the nimforum.
|
||||
|
||||
import strutils, db_sqlite, os, times, json
|
||||
|
||||
import auth, frontend/user
|
||||
|
||||
proc backup(path: string) =
|
||||
if existsFile(path):
|
||||
let backupPath = path & "." & $getTime().toUnix()
|
||||
echo(path, " already exists. Moving to ", backupPath)
|
||||
moveFile(path, backupPath)
|
||||
|
||||
proc initialiseDb(admin: tuple[username, password, email: string]) =
|
||||
let path = getCurrentDir() / "nimforum.db"
|
||||
backup(path)
|
||||
|
||||
var db = open(connection="nimforum.db", user="", password="",
|
||||
database="nimforum")
|
||||
|
||||
const
|
||||
userNameType = "varchar(20)"
|
||||
passwordType = "varchar(50)"
|
||||
emailType = "varchar(254)" # https://stackoverflow.com/a/574698/492186
|
||||
|
||||
# -- Category
|
||||
|
||||
db.exec(sql"""
|
||||
create table category(
|
||||
id integer primary key,
|
||||
name varchar(100) not null,
|
||||
description varchar(500) not null,
|
||||
color varchar(10) not null
|
||||
);
|
||||
|
||||
insert into category (id, name, description, color)
|
||||
values (0, 'Default', '', '');
|
||||
""")
|
||||
|
||||
# -- Thread
|
||||
|
||||
db.exec(sql"""
|
||||
create table thread(
|
||||
id integer primary key,
|
||||
name varchar(100) not null,
|
||||
views integer not null,
|
||||
modified timestamp not null default (DATETIME('now')),
|
||||
category integer not null default 0,
|
||||
isLocked boolean not null default 0,
|
||||
solution integer,
|
||||
isDeleted boolean not null default 0,
|
||||
|
||||
foreign key (category) references category(id),
|
||||
foreign key (solution) references post(id)
|
||||
);""", [])
|
||||
|
||||
db.exec(sql"""
|
||||
create unique index ThreadNameIx on thread (name);
|
||||
""", [])
|
||||
|
||||
# -- Person
|
||||
|
||||
db.exec(sql("""
|
||||
create table person(
|
||||
id integer primary key,
|
||||
name $# not null,
|
||||
password $# not null,
|
||||
email $# not null,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
salt varbin(128) not null,
|
||||
status varchar(30) not null,
|
||||
lastOnline timestamp not null default (DATETIME('now')),
|
||||
isDeleted boolean not null default 0
|
||||
);""" % [userNameType, passwordType, emailType]), [])
|
||||
|
||||
db.exec(sql"""
|
||||
create unique index UserNameIx on person (name);
|
||||
""", [])
|
||||
db.exec sql"create index PersonStatusIdx on person(status);"
|
||||
|
||||
# Create default user.
|
||||
let salt = makeSalt()
|
||||
let password = makePassword(admin.password, salt)
|
||||
db.exec(sql"""
|
||||
insert into person (id, name, password, email, salt, status)
|
||||
values (0, ?, ?, ?, ?, ?);
|
||||
""", admin.username, password, admin.email, salt, $Admin)
|
||||
|
||||
# -- Post
|
||||
|
||||
db.exec(sql"""
|
||||
create table post(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
ip inet not null,
|
||||
content varchar(1000) not null,
|
||||
thread integer not null,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
isDeleted boolean not null default 0,
|
||||
|
||||
foreign key (thread) references thread(id),
|
||||
foreign key (author) references person(id)
|
||||
);""", [])
|
||||
|
||||
db.exec sql"create index PostByAuthorIdx on post(thread, author);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table postRevision(
|
||||
id integer primary key,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
original integer not null,
|
||||
content varchar(1000) not null,
|
||||
|
||||
foreign key (original) references post(id)
|
||||
)
|
||||
""")
|
||||
|
||||
# -- Session
|
||||
|
||||
db.exec(sql("""
|
||||
create table session(
|
||||
id integer primary key,
|
||||
ip inet not null,
|
||||
password $# not null,
|
||||
userid integer not null,
|
||||
lastModified timestamp not null default (DATETIME('now')),
|
||||
foreign key (userid) references person(id)
|
||||
);""" % [passwordType]), [])
|
||||
|
||||
# -- Likes
|
||||
|
||||
db.exec(sql("""
|
||||
create table like(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
post integer not null,
|
||||
creation timestamp not null default (DATETIME('now')),
|
||||
|
||||
foreign key (author) references person(id),
|
||||
foreign key (post) references post(id)
|
||||
)
|
||||
"""))
|
||||
|
||||
# -- Report
|
||||
|
||||
db.exec(sql("""
|
||||
create table report(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
post integer not null,
|
||||
kind varchar(30) not null,
|
||||
content varchar(500) not null default '',
|
||||
|
||||
foreign key (author) references person(id),
|
||||
foreign key (post) references post(id)
|
||||
)
|
||||
"""))
|
||||
|
||||
# -- FTS
|
||||
|
||||
if not db.tryExec(sql"""
|
||||
CREATE VIRTUAL TABLE thread_fts USING fts4 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL
|
||||
);""", []):
|
||||
echo "thread_fts table already exists or fts4 not supported"
|
||||
else:
|
||||
db.exec(sql"""
|
||||
INSERT INTO thread_fts
|
||||
SELECT id, name FROM thread;
|
||||
""", [])
|
||||
if not db.tryExec(sql"""
|
||||
CREATE VIRTUAL TABLE post_fts USING fts4 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
content VARCHAR(1000) NOT NULL
|
||||
);""", []):
|
||||
echo "post_fts table already exists or fts4 not supported"
|
||||
else:
|
||||
db.exec(sql"""
|
||||
INSERT INTO post_fts
|
||||
SELECT id, content FROM post;
|
||||
""", [])
|
||||
|
||||
close(db)
|
||||
|
||||
proc initialiseConfig(
|
||||
name, hostname: string,
|
||||
recaptcha: tuple[siteKey, secretKey: string],
|
||||
smtp: tuple[address, user, password: string],
|
||||
isDev: bool
|
||||
) =
|
||||
let path = getCurrentDir() / "forum.json"
|
||||
backup(path)
|
||||
|
||||
var j = %{
|
||||
"name": %name,
|
||||
"hostname": %hostname,
|
||||
"recaptchaSiteKey": %recaptcha.siteKey,
|
||||
"recaptchaSecretKey": %recaptcha.secretKey,
|
||||
"smtpAddress": %smtp.address,
|
||||
"smtpUser": %smtp.user,
|
||||
"smtpPassword": %smtp.password,
|
||||
"isDev": %isDev
|
||||
}
|
||||
|
||||
writeFile(path, $j)
|
||||
|
||||
when isMainModule:
|
||||
if paramCount() > 0 and paramStr(1) == "--dev":
|
||||
echo("Initialising nimforum for development...")
|
||||
initialiseConfig(
|
||||
"Development Forum",
|
||||
"localhost.local",
|
||||
recaptcha=("", ""),
|
||||
smtp=("", "", ""),
|
||||
isDev=true
|
||||
)
|
||||
|
||||
initialiseDb(
|
||||
admin=("admin", "admin", "admin@localhost.local")
|
||||
)
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
Forum content license
|
||||
=====================
|
||||
|
||||
All the content contributed to the Nimrod Forum is `cc-wiki (aka cc-by-sa)
|
||||
All the content contributed to the Nim Forum is `cc-wiki (aka cc-by-sa)
|
||||
<http://creativecommons.org/licenses/by-sa/3.0/>`_ licensed, intended to be
|
||||
**shared and remixed**. In the future we may even provide all this data as a
|
||||
convenient data dump.
|
||||
|
|
@ -16,13 +16,13 @@ attribution**::
|
|||
Let us clarify what we mean by attribution. If you republish this content, we
|
||||
require that you:
|
||||
|
||||
* **Visually indicate that the content is from the Nimrod Forum**. It doesn’t
|
||||
* **Visually indicate that the content is from the Nim Forum**. It doesn’t
|
||||
have to be obnoxious; a discreet text blurb is fine.
|
||||
* **Hyperlink directly to the original post** (e.g.,
|
||||
http://forum.nimrod-lang.org/t/186)
|
||||
http://forum.nim-lang.org/t/186)
|
||||
* **Show the author names** for every post.
|
||||
* **Hyperlink each author name** directly back to their user profile page
|
||||
(e.g., http://forum.nimrod-lang.org/profile/Araq)
|
||||
(e.g., http://forum.nim-lang.org/profile/Araq)
|
||||
|
||||
By “directly”, we mean each hyperlink must point directly to our domain in
|
||||
standard HTML visible even with JavaScript disabled, and not use a tinyurl or
|
||||
|
|
@ -38,5 +38,5 @@ Feel free to remix and reuse to your heart’s content, as long as a good faith
|
|||
effort is made to attribute the content!
|
||||
|
||||
Content previous to the forum license change of
|
||||
http://forum.nimrod-lang.org/t/186 remains under the original authors'
|
||||
http://forum.nim-lang.org/t/186 remains under the original authors'
|
||||
copyright, and therefore you cannot reuse it.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ type
|
|||
mlistAddress: string
|
||||
recaptchaSecretKey*: string
|
||||
recaptchaSiteKey*: string
|
||||
isDev*: bool
|
||||
|
||||
var docConfig: StringTableRef
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ proc loadConfig*(filename = getCurrentDir() / "forum.json"): Config =
|
|||
result.mlistAddress = root{"mlistAddress"}.getStr("")
|
||||
result.recaptchaSecretKey = root{"recaptchaSecretKey"}.getStr("")
|
||||
result.recaptchaSiteKey = root{"recaptchaSiteKey"}.getStr("")
|
||||
result.isDev = root{"isDev"}.getBool()
|
||||
except:
|
||||
echo("[WARNING] Couldn't read config file: ", filename)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue