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
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:
@@ -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"
|
||||
Reference in New Issue
Block a user