Add Ormin ORM support for BaraDB (Nim client)
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled

This commit is contained in:
2026-05-15 21:49:08 +03:00
parent 2e945c1dcb
commit 4e54075078
63 changed files with 9764 additions and 0 deletions
@@ -0,0 +1,19 @@
create table if not exists users(
id integer primary key,
name varchar(20) not null,
password varchar(32) not null,
creation timestamp not null default (DATETIME('now')),
lastOnline timestamp not null default (DATETIME('now'))
);
/* Names need to be unique: */
create unique index if not exists UserNameIx on users(name);
create table if not exists messages(
id integer primary key,
author integer not null,
content varchar(1000) not null,
creation timestamp not null default (DATETIME('now')),
foreign key (author) references users(id)
);
@@ -0,0 +1,9 @@
import strutils, db_sqlite
var db = open(connection="chat.db", user="araq", password="",
database="chat")
let model = readFile("chat_model.sql")
for m in model.split(';'):
if m.strip != "":
db.exec(sql(m), [])
db.close()
@@ -0,0 +1,92 @@
## Frontend for our chat application example.
## You need to have 'karax' installed in order for this to work.
import karax / [kbase, karax, vdom, karaxdsl, kajax, jwebsockets, jjson,
jstrutils, errors]
# Custom UI element with input validation:
proc validateNotEmpty(field: kstring): proc () =
result = proc () =
let x = getVNodeById(field)
if x.text.isNil or x.text == "":
setError(field, field & " must not be empty")
else:
setError(field, "")
proc loginField(desc, field, class: kstring;
validator: proc (field: kstring): proc ()): VNode =
result = buildHtml(tdiv):
label(`for` = field):
text desc
input(`type` = class, id = field, onchange = validator(field))
# here we setup the connection to the server:
let conn = newWebSocket("ws://localhost:8080", "orminchat")
proc send(msg: JsonNode) =
# The Ormin "protocol" requires us to have 'send' implementation.
conn.send(toJson(msg))
type User* = ref object
name*, password: kstring
const
username = kstring"username"
password = kstring"password"
message = kstring"message"
# Include the 'chatclient' helper that Ormin produced for us:
include chatclient
template loggedIn(): bool = userId > 0
proc doLogin() =
registerUser(User(name: getVNodeById(username).text,
password: getVNodeById(password).text))
proc doSendMessage() =
let inputField = getVNodeById(message)
sendMessage(TextMessage(author: userId, content: inputField.text))
inputField.setInputText ""
proc registerOnUpdate() =
conn.onmessage =
proc (e: MessageEvent) =
let msg = fromJson[JsonNode](e.data)
# 'recvMsg' was generated for us:
recvMsg(msg)
karax.redraw()
proc main(): VNode =
result = buildHtml(tdiv):
tdiv:
if not loggedIn:
loginField("Name :", username, "input", validateNotEmpty)
loginField("Password: ", password, "password", validateNotEmpty)
button(onclick = doLogin, disabled = disableOnError()):
text "Login"
p:
text getError(username)
p:
text getError(password)
tdiv:
table:
for m in allMessages:
tr:
td:
bold:
text m.name
td:
text m.content
tdiv:
if loggedIn:
label(`for` = message):
text "Message: "
input(class = "input", id = message, onkeyupenter = doSendMessage)
registerOnUpdate()
runLater proc() =
getRecentMessages()
setRenderer(main)
@@ -0,0 +1,96 @@
import ../../ormin / [serverws]
import ../../ormin
import json
# Ormin needs to know about our SQL model:
importModel(DbBackend.sqlite, "chat_model")
# Currently Ormin assumes a global database connection that needs to be
# annotated as '.global' for technical reasons. Later versions will improve.
var db {.global.} = open("chat.db", "", "", "")
# Ormin produces the file "chatclient.nim" for our frontend:
protocol "chatclient.nim":
# A 'common' code section is shared by both the client and the server:
common:
when not defined(js):
type kstring = string
type
inetType = kstring
varcharType = kstring
timestampType = kstring
intType = int
server "get recent messages":
let lastMessages = query:
select messages(content, creation, author)
join users(name)
orderby desc(creation)
limit 100
send(lastMessages)
client "get recent messages":
type TextMessage* = ref object
# Ormin fills in the fields of 'TextMessage' for us, based on the
# query in the 'server' part.
var allMessages*: seq[TextMessage] = @[]
proc getRecentMessages*()
allMessages = recv()
server "send chat message":
let userId = arg["author"].num
# unregistered users cannot send anything:
if userId == 0: return
query:
insert messages(content = %arg["content"], author = ?userId)
query:
update users(lastOnline = !!"DATETIME('now')")
where id == ?userId
let lastMessage = query:
select messages(content, creation, author)
join users(name)
orderby desc(creation)
limit 1
broadcast(lastMessage)
client "send chat message":
proc sendMessage*(m: TextMessage)
allMessages.add recv(TextMessage)
# This is the request to register/login a new user:
server "register/login a new user":
var candidates = query:
produce nim
select users(id, password)
where name == %arg["name"]
if candidates.len == 0:
let userId = query:
produce nim
insert users(name = %arg["name"], password = %arg["password"])
returning id
send(%userId)
else:
block search:
for c in candidates:
if c[1] == arg["password"].str:
send(%c[0])
echo "login found!"
break search
# invalid login:
send(%0)
echo "no such user!"
client "register/login a new user":
proc registerUser*(u: User)
var userId: int
userId = recv()
if userId == 0: setError(username, "invalid login!")
serve "orminchat", dispatch
@@ -0,0 +1,2 @@
--define: ssl
--path:"$projectDir/../../../ormin"