Initial commit: nimforum with BaraDB backend
- Replaced SQLite with BaraDB via TCP wire protocol - Added baradb_client.nim and baradb_sqlite.nim adapter - Renamed SQL keywords: status->usrStatus, key->sessionKey, like->postLike - Added NOW() support for datetime defaults - Switched Jester to asynchttpserver (-d:useStdLib) to avoid thread/GC issues - Frontend built and favicon added
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
#
|
||||
#
|
||||
# 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, os, times, json, options, terminal
|
||||
|
||||
import baradb_sqlite
|
||||
|
||||
import auth, frontend/user
|
||||
|
||||
proc backup(path: string, contents: Option[string]=none[string]()) =
|
||||
if fileExists(path):
|
||||
if contents.isSome() and readFile(path) == contents.get():
|
||||
# Don't backup if the files are equivalent.
|
||||
echo("Not backing up because new file is the same.")
|
||||
return
|
||||
|
||||
let backupPath = path & "." & $getTime().toUnix()
|
||||
echo(path, " already exists. Moving to ", backupPath)
|
||||
moveFile(path, backupPath)
|
||||
|
||||
proc createUser(db: DbConn, user: tuple[username, password, email: string],
|
||||
rank: Rank) =
|
||||
assert user.username.len != 0
|
||||
let salt = makeSalt()
|
||||
let password = makePassword(user.password, salt)
|
||||
|
||||
let id = db.nextId("person")
|
||||
echo "createUser: ", user.username, " id=", id
|
||||
exec(db, sql"""
|
||||
INSERT INTO person(id, name, password, email, salt, usrStatus, creation, lastOnline, previousVisitAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW(), NOW())
|
||||
""", $id, user.username, password, user.email, salt, $rank)
|
||||
echo "createUser done: ", user.username
|
||||
|
||||
proc initialiseDb(admin: tuple[username, password, email: string],
|
||||
filename="nimforum.db") =
|
||||
echo "initialiseDb starting..."
|
||||
let
|
||||
path = getCurrentDir() / filename
|
||||
isTest = "-test" in filename
|
||||
isDev = "-dev" in filename
|
||||
|
||||
if not isDev and not isTest:
|
||||
backup(path)
|
||||
|
||||
echo "Removing file..."
|
||||
removeFile(path)
|
||||
echo "Opening DB..."
|
||||
var db = open(connection="127.0.0.1:9472", user="", password="",
|
||||
database="nimforum")
|
||||
echo "DB opened"
|
||||
|
||||
const
|
||||
userNameType = "varchar(20)"
|
||||
passwordType = "varchar(300)"
|
||||
emailType = "varchar(254)" # https://stackoverflow.com/a/574698/492186
|
||||
|
||||
# -- Category
|
||||
echo "Creating category table..."
|
||||
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
|
||||
);
|
||||
""")
|
||||
|
||||
db.exec(sql"""
|
||||
insert into category (id, name, description, color)
|
||||
values (0, 'Unsorted', 'No category has been chosen yet.', '000000');
|
||||
""")
|
||||
|
||||
# -- Thread
|
||||
|
||||
db.exec(sql"""
|
||||
create table thread(
|
||||
id integer primary key,
|
||||
name varchar(100) not null,
|
||||
views integer not null,
|
||||
modified datetime not null default '1970-01-01 00:00:00',
|
||||
category integer not null default 0,
|
||||
isLocked boolean not null default 0,
|
||||
solution integer,
|
||||
isDeleted boolean not null default 0,
|
||||
isPinned 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 datetime not null default '1970-01-01 00:00:00',
|
||||
salt varbin(128) not null,
|
||||
usrStatus varchar(30) not null,
|
||||
lastOnline datetime not null default '1970-01-01 00:00:00',
|
||||
previousVisitAt datetime not null default '1970-01-01 00:00:00',
|
||||
isDeleted boolean not null default 0,
|
||||
needsPasswordReset 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(usrStatus);"
|
||||
|
||||
# Create default user.
|
||||
echo "Creating admin..."
|
||||
db.createUser(admin, Admin)
|
||||
|
||||
# Create some test data for development
|
||||
if isTest or isDev:
|
||||
echo "Creating dev users..."
|
||||
for rank in AutoSpammer..Moderator:
|
||||
let rankLower = toLowerAscii($rank)
|
||||
let user = (username: $rankLower,
|
||||
password: $rankLower,
|
||||
email: $rankLower & "@localhost.local")
|
||||
echo "Creating user: ", rankLower
|
||||
db.createUser(user, rank)
|
||||
|
||||
echo "Inserting categories..."
|
||||
db.exec(sql"""
|
||||
insert into category (id, name, description, color)
|
||||
values (1, 'Libraries', 'Libraries and library development', '0198E1'),
|
||||
(2, 'Announcements', 'Announcements by Nim core devs', 'FFEB3B'),
|
||||
(3, 'Fun', 'Posts that are just for fun', '00897B'),
|
||||
(4, 'Potential Issues', 'Potential Nim compiler issues', 'E53935');
|
||||
""")
|
||||
|
||||
# -- Post
|
||||
|
||||
db.exec(sql"""
|
||||
create table post(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
ip string not null,
|
||||
content varchar(1000) not null,
|
||||
thread integer not null,
|
||||
creation datetime not null default '1970-01-01 00:00:00',
|
||||
isDeleted boolean not null default 0,
|
||||
replyingTo integer,
|
||||
|
||||
foreign key (thread) references thread(id),
|
||||
foreign key (author) references person(id),
|
||||
foreign key (replyingTo) references post(id)
|
||||
);""", [])
|
||||
|
||||
db.exec sql"create index PostByAuthorIdx on post(thread, author);"
|
||||
|
||||
db.exec(sql"""
|
||||
create table postRevision(
|
||||
id integer primary key,
|
||||
creation datetime not null default '1970-01-01 00:00:00',
|
||||
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 string not null,
|
||||
sessionKey $# not null,
|
||||
userid integer not null,
|
||||
lastModified datetime not null default '1970-01-01 00:00:00',
|
||||
foreign key (userid) references person(id)
|
||||
);""" % [passwordType]), [])
|
||||
|
||||
# -- Likes
|
||||
|
||||
db.exec(sql("""
|
||||
create table postLike(
|
||||
id integer primary key,
|
||||
author integer not null,
|
||||
post integer not null,
|
||||
creation datetime not null default '1970-01-01 00:00:00',
|
||||
|
||||
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 disabled for BaraDB demo
|
||||
echo "FTS tables skipped (BaraDB does not support fts4)"
|
||||
|
||||
close(db)
|
||||
|
||||
proc initialiseConfig(
|
||||
name, title, hostname: string,
|
||||
recaptcha: tuple[siteKey, secretKey: string],
|
||||
smtp: tuple[address, user, password, fromAddr: string, tls: bool],
|
||||
isDev: bool,
|
||||
dbPath: string,
|
||||
ga: string=""
|
||||
) =
|
||||
let path = getCurrentDir() / "forum.json"
|
||||
|
||||
var j = %{
|
||||
"name": %name,
|
||||
"title": %title,
|
||||
"hostname": %hostname,
|
||||
"recaptchaSiteKey": %recaptcha.siteKey,
|
||||
"recaptchaSecretKey": %recaptcha.secretKey,
|
||||
"smtpAddress": %smtp.address,
|
||||
"smtpUser": %smtp.user,
|
||||
"smtpPassword": %smtp.password,
|
||||
"smtpFromAddr": %smtp.fromAddr,
|
||||
"smtpTls": %smtp.tls,
|
||||
"isDev": %isDev,
|
||||
"dbPath": %dbPath
|
||||
}
|
||||
if ga.len > 0:
|
||||
j["ga"] = %ga
|
||||
|
||||
backup(path, some(pretty(j)))
|
||||
writeFile(path, pretty(j))
|
||||
|
||||
proc question(q: string): string =
|
||||
while result.len == 0:
|
||||
stdout.write(q)
|
||||
result = stdin.readLine()
|
||||
|
||||
proc setup() =
|
||||
echo("""
|
||||
Welcome to the NimForum setup script. Please answer the following questions.
|
||||
These can be changed later in the generated forum.json file.
|
||||
""")
|
||||
|
||||
let name = question("Forum full name: ")
|
||||
let title = question("Forum short name: ")
|
||||
|
||||
let hostname = question("Forum hostname: ")
|
||||
|
||||
let adminUser = question("Admin username: ")
|
||||
let adminPass = readPasswordFromStdin("Admin password: ")
|
||||
let adminEmail = question("Admin email: ")
|
||||
|
||||
echo("")
|
||||
echo("The following question are related to recaptcha. \nYou must set up a " &
|
||||
"recaptcha v2 for your forum before answering them. \nPlease do so now " &
|
||||
"and then answer these questions: https://www.google.com/recaptcha/admin")
|
||||
let recaptchaSiteKey = question("Recaptcha site key: ")
|
||||
let recaptchaSecretKey = question("Recaptcha secret key: ")
|
||||
|
||||
|
||||
echo("The following questions are related to smtp. You must set up a \n" &
|
||||
"mailing server for your forum or use an external service.")
|
||||
let smtpAddress = question("SMTP address (eg: mail.hostname.com): ")
|
||||
let smtpUser = question("SMTP user: ")
|
||||
let smtpPassword = readPasswordFromStdin("SMTP pass: ")
|
||||
let smtpFromAddr = question("SMTP sending email address (eg: mail@mail.hostname.com): ")
|
||||
let smtpTls = parseBool(question("Enable TLS for SMTP: "))
|
||||
|
||||
echo("The following is optional. You can specify your Google Analytics ID " &
|
||||
"if you wish. Otherwise just leave it blank.")
|
||||
stdout.write("Google Analytics (eg: UA-12345678-1): ")
|
||||
let ga = stdin.readLine().strip()
|
||||
|
||||
let dbPath = "nimforum.db"
|
||||
initialiseConfig(
|
||||
name, title, hostname, (recaptchaSiteKey, recaptchaSecretKey),
|
||||
(smtpAddress, smtpUser, smtpPassword, smtpFromAddr, smtpTls), isDev=false,
|
||||
dbPath, ga
|
||||
)
|
||||
|
||||
initialiseDb(
|
||||
admin=(adminUser, adminPass, adminEmail),
|
||||
dbPath
|
||||
)
|
||||
|
||||
echo("Setup complete!")
|
||||
|
||||
proc echoHelp() =
|
||||
quit("""
|
||||
Usage: setup_nimforum opts
|
||||
|
||||
Options:
|
||||
--setup Performs first time setup for end users.
|
||||
|
||||
Development options:
|
||||
--dev Creates a new development DB and config.
|
||||
--test Creates a new test DB and config.
|
||||
--blank Creates a new blank DB.
|
||||
""")
|
||||
|
||||
when isMainModule:
|
||||
if paramCount() > 0:
|
||||
case paramStr(1)
|
||||
of "--dev":
|
||||
let dbPath = "nimforum-dev.db"
|
||||
echo("Initialising nimforum for development...")
|
||||
initialiseConfig(
|
||||
"Development Forum",
|
||||
"Development Forum",
|
||||
"localhost",
|
||||
recaptcha=("", ""),
|
||||
smtp=("", "", "", "", false),
|
||||
isDev=true,
|
||||
dbPath
|
||||
)
|
||||
|
||||
initialiseDb(
|
||||
admin=("admin", "admin", "admin@localhost.local"),
|
||||
dbPath
|
||||
)
|
||||
of "--test":
|
||||
let dbPath = "nimforum-test.db"
|
||||
echo("Initialising nimforum for testing...")
|
||||
initialiseConfig(
|
||||
"Test Forum",
|
||||
"Test Forum",
|
||||
"localhost",
|
||||
recaptcha=("", ""),
|
||||
smtp=("", "", "", "", false),
|
||||
isDev=true,
|
||||
dbPath
|
||||
)
|
||||
|
||||
initialiseDb(
|
||||
admin=("admin", "admin", "admin@localhost.local"),
|
||||
dbPath
|
||||
)
|
||||
of "--blank":
|
||||
let dbPath = "nimforum-blank.db"
|
||||
echo("Initialising blank DB...")
|
||||
initialiseDb(
|
||||
admin=("", "", ""),
|
||||
dbPath
|
||||
)
|
||||
of "--setup":
|
||||
setup()
|
||||
else:
|
||||
echoHelp()
|
||||
else:
|
||||
echoHelp()
|
||||
|
||||
quit()
|
||||
Reference in New Issue
Block a user