Initial commit: nimforum with BaraDB backend
CI / test_stable_and_devel (stable) (push) Has been cancelled
CI / test_stable_and_devel (stable, devel) (push) Has been cancelled
CI / test_stable_and_devel (stable, stable) (push) Has been cancelled

- 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:
2026-05-15 17:24:24 +03:00
commit 570d53f391
80 changed files with 29916 additions and 0 deletions
+374
View File
@@ -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()