nim-theNewWeb/main.nim

406 lines
12 KiB
Nim
Raw Normal View History

2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- #
# Bruno Charest
# 2022-08-09
#
# __ DESCRIPTIONS __
# main : stating point of the program
#
# Project that create a web interface for Joplin base on
# Joplin Terminal running in background
#
# Inspiration of : https://ttj.dk/blog/2019/01/20/setup-a-website-with-nim
2022-07-31 16:45:47 -04:00
# Copyright 2019 - Thomas T. Jarløv
# [LINK] debug url : http://127.0.0.1:7000/
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# Import section
# --==--==--==--==--==--==--==--==--==--==-- #
import os # Used to get arguments
import uri # We need to encode urls: encodeUrl()
import times # Time and date
2022-07-31 16:45:47 -04:00
import jester # Our webserver
import logging # Logging utils
import strutils # Basic functions
2022-08-10 01:59:10 -04:00
import parsecfg # Parse CFG (config) files
import std/json # json manipulation
import db_sqlite # SQLite
2022-07-31 16:45:47 -04:00
import code/database_utils # Utils used in the database
import code/password_utils # Our file with password utils
2022-08-10 01:59:10 -04:00
import code/joplin_utils # Joplin utils procedures and types
import code/web_utils # Web utils procedures and types
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# First we'll load config files
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
let dict = loadConfig("config/config.cfg")
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# Get parameters from config.cfg
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# Database
2022-07-31 16:45:47 -04:00
let db_user = dict.getSectionValue("Database", "user")
let db_pass = dict.getSectionValue("Database", "pass")
let db_name = dict.getSectionValue("Database", "name")
let db_host = dict.getSectionValue("Database", "host")
2022-08-10 01:59:10 -04:00
# Website
2022-07-31 16:45:47 -04:00
let mainURL = dict.getSectionValue("Server", "url")
let mainPort = parseInt dict.getSectionValue("Server", "port")
let mainWebsite = dict.getSectionValue("Server", "website")
2022-08-10 01:59:10 -04:00
# Joplin
let joplin_token = dict.getSectionValue("Joplin", "token")
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# Database var
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
var db: DbConn
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# Jester setting server settings
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
settings:
port = Port(mainPort)
bindAddr = mainURL
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# Setup user data
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
type
TData* = ref object of RootObj
loggedIn*: bool
userid, username*, userpass*, email*: string
req*: Request
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# proc init : initialisation variables
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
proc init(c: var TData, cld: var ColomnLeftData) =
2022-07-31 16:45:47 -04:00
## Empty out user session data
c.userpass = ""
c.username = ""
c.userid = ""
c.loggedIn = false
2022-08-06 16:51:49 -04:00
## default option
# cld.option = notes
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# function : loggedIn
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
func loggedIn(c: TData): bool =
## Check if user is logged in by verifying that c.username exists
c.username.len > 0
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# proc checkLoggedIn
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
proc checkLoggedIn(c: var TData) =
## Check if user is logged in
# Get the users cookie named `sid`. If it does not exist, return
if not c.req.cookies.hasKey("sid"): return
# Assign cookie to `let sid`
let sid = c.req.cookies["sid"]
# Update the value lastModified for the user in the
# table session where the sid and IP match. If there's
# any results (above 0) assign values
if execAffectedRows(db, sql("UPDATE session SET lastModified = " & $toInt(epochTime()) & " " & "WHERE ip = ? AND key = ?"), c.req.ip, sid) > 0:
# Get user data based on userID from session table
# Assign values to user details - `c`
c.userid = getValue(db, sql"SELECT userid FROM session WHERE ip = ? AND key = ?", c.req.ip, sid)
# Get user data based on userID from person table
let row = getRow(db, sql"SELECT name, email, status FROM person WHERE id = ?", c.userid)
# Assign user data
c.username = row[0]
c.email = toLowerAscii(row[1])
# Update our session table with info about activity
discard tryExec(db, sql"UPDATE person SET lastOnline = ? WHERE id = ?", toInt(epochTime()), c.userid)
else:
# If the user is not found in the session table
c.loggedIn = false
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# proc login user
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
proc login(c: var TData, email, pass: string): tuple[b: bool, s: string] =
## User login
# We have predefined query
const query = sql"SELECT id, name, password, email, salt, status FROM person WHERE email = ?"
# If the email or pass passed in the proc's parameters is empty, fail
if email.len == 0 or pass.len == 0:
return (false, "Missing password or username")
# We'll use fastRows for a quick query.
# Notice that the email is set to lower ascii
# to avoid problems if the user has any
# capitalized letters.
for row in fastRows(db, query, toLowerAscii(email)):
# Now our password library is going to work. It'll
# check the password against the hashed password
# and salt.
if row[2] == makePassword(pass, row[4], row[2]):
# Assign the values
c.userid = row[0]
c.username = row[1]
c.userpass = row[2]
c.email = toLowerAscii(row[3])
# Generate session key and save it
let key = makeSessionKey()
exec(db, sql"INSERT INTO session (ip, key, userid) VALUES (?, ?, ?)", c.req.ip, key, row[0])
info("Login successful")
return (true, key)
info("Login failed")
return (false, "Login failed")
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# proc logout user
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
proc logout(c: var TData) =
## Logout
c.username = ""
c.userpass = ""
const query = sql"DELETE FROM session WHERE ip = ? AND key = ?"
exec(db, query, c.req.ip, c.req.cookies["sid"])
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# Do the check inside our routes
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
template createTFD() =
## Check if logged in and assign data to user
# Assign the c to TDATA
var c {.inject.}: TData
2022-08-06 16:51:49 -04:00
# Assign the c to TDATA
var cld {.inject.}: ColomnLeftData
2022-07-31 16:45:47 -04:00
# New instance of c
new(c)
2022-08-06 16:51:49 -04:00
new(cld)
2022-07-31 16:45:47 -04:00
# Set standard values
2022-08-06 16:51:49 -04:00
init(c,cld)
2022-07-31 16:45:47 -04:00
# Get users request
c.req = request
2022-08-06 16:51:49 -04:00
cld.req = request
2022-07-31 16:45:47 -04:00
# Check for cookies (we need the cookie named sid)
if cookies(request).len > 0:
# Check if user is logged in
checkLoggedIn(c)
# Use the func()
c.loggedIn = loggedIn(c)
# isMainModule
2022-08-10 01:59:10 -04:00
# ---------------------------- #
2022-07-31 16:45:47 -04:00
when isMainModule:
echo "Nim Web is now running: " & $now()
# Generate DB if newdb is in the arguments
# or if the database does not exists
if "newdb" in commandLineParams() or not fileExists(db_host):
generateDB()
quit()
# Connect to DB
try:
# We are using the values which we assigned earlier
db = open(connection=db_host, user=db_user, password=db_pass, database=db_name)
info("Connection to DB is established.")
except:
fatal("Connection to DB could not be established.")
sleep(5_000)
quit()
# Add an admin user if newuser is in the args
if "newuser" in commandLineParams():
createAdminUser(db, commandLineParams())
quit()
# Include template files
2022-08-10 01:59:10 -04:00
# ---------------------------- #
2022-07-31 16:45:47 -04:00
#include "tmpl/main.tmpl"
include "tmpl/user.tmpl"
include "tmpl/website.tmpl"
2022-08-06 16:51:49 -04:00
# Tests pages include
2022-08-10 01:59:10 -04:00
# ---------------------------- #
2022-08-06 16:51:49 -04:00
include "tmpl/tests/test_homepage.tmpl"
include "tmpl/tests/test_ping.tmpl"
include "tmpl/tests/test_notebooks.tmpl"
include "tmpl/tests/test_notes.tmpl"
include "tmpl/tests/test_tags.tmpl"
include "tmpl/tests/test_viewtree.tmpl"
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
# Setup routes (URL's)
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
routes:
2022-08-10 01:59:10 -04:00
# default route
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
get "/":
createTFD()
resp genMain(c)
2022-08-10 01:59:10 -04:00
# master site once login
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
get "/secret":
createTFD()
echo c.loggedIn
2022-08-06 16:51:49 -04:00
echo @"msg"
2022-07-31 16:45:47 -04:00
if c.loggedIn:
2022-08-06 16:51:49 -04:00
# if Joplin application work
var checkJoplin = waitFor ping_joplin(joplin_token)
if checkJoplin.ping_status:
cld.j_status = true
else:
cld.j_status = false
# determine the section to uptade
if @"msg" == "newNote":
cld.option = newNote
echo "Todo"
elif @"msg" == "search":
cld.option = search
echo "Todo"
elif @"msg" == "shortcuts":
cld.option = shortcuts
echo "Todo"
elif @"msg" == "notebooks":
cld.option = notebooks
cld.j_notebooks = waitFor get_joplin_notebooks(joplin_token)
elif @"msg" == "notes":
cld.option = notes
cld.j_notes = waitFor get_joplin_notes(joplin_token)
elif @"msg" == "tags":
cld.option = tags
cld.j_tags = waitFor get_joplin_tags(joplin_token)
elif @"msg" == "sendFeedBack":
echo "Todo"
resp Http200, {"Access-Control-Allow-Origin": "http://127.0.0.1:7000"}, genSecret(c,cld)
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# Login route
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
get "/login":
createTFD()
resp Http200, {"Access-Control-Allow-Origin": "http://127.0.0.1:7000"}, genLogin(c, @"msg")
2022-07-31 16:45:47 -04:00
2022-08-10 01:59:10 -04:00
# action route during login
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
post "/dologin":
createTFD()
let (loginB, loginS) = login(c, replace(toLowerAscii(@"email"), " ", ""), replace(@"password", " ", ""))
if loginB:
when defined(dev):
jester.setCookie("sid", loginS, daysForward(7))
else:
jester.setCookie("sid", loginS, daysForward(7), samesite = Lax, secure = true, httpOnly = true)
#jester.setCookie("sid", loginS, daysForward(7), samesite = Lax, secure = true, httpOnly = true)
2022-07-31 16:45:47 -04:00
redirect("/secret")
else:
redirect("/login?msg=" & encodeUrl(loginS))
2022-08-10 01:59:10 -04:00
# Logout route
# --==--==--==--==--==--==--==--==--==--==-- #
2022-07-31 16:45:47 -04:00
get "/logout":
createTFD()
logout(c)
redirect("/")
2022-08-10 01:59:10 -04:00
# --==--==--==--==--==--==--==--==--==--==-- #
# # ROUTES TESTS SECTION ##
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
2022-08-10 01:59:10 -04:00
# master tests page
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
get "/test":
createTFD()
resp test_homepage(c)
2022-08-10 01:59:10 -04:00
# Test ping joplin - query api validation
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
get "/test_pingjoplin":
createTFD()
var pingCheck: joplin_ping
pingCheck = waitFor ping_joplin(joplin_token)
echo pingCheck.ping_status
resp test_ping(c, pingCheck)
2022-08-10 01:59:10 -04:00
# Test geting list of all notebooks
# --==--==--==--==--==--==--==--==--==--==-- #
get "/test_notebooks":
createTFD()
2022-08-06 16:51:49 -04:00
cld.j_notebooks = waitFor get_joplin_notebooks(joplin_token)
resp test_notebooks(c, cld)
2022-08-10 01:59:10 -04:00
# Test geting list of all notes
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
get "/test_notes":
createTFD()
cld.j_notes = waitFor get_joplin_notes(joplin_token)
resp test_notes(c, cld)
2022-08-10 01:59:10 -04:00
# Test geting list of all tags
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
get "/test_tags":
createTFD()
cld.j_tags = waitFor get_joplin_tags(joplin_token)
resp test_tags(c, cld)
2022-08-10 01:59:10 -04:00
# Test a viewtree
# --==--==--==--==--==--==--==--==--==--==-- #
2022-08-06 16:51:49 -04:00
get "/test_viewtree":
createTFD()
resp test_viewtree(c)
2022-08-10 01:59:10 -04:00
# Test geting all tags as JSON output
# --==--==--==--==--==--==--==--==--==--==-- #
get "/test_tags_json":
createTFD()
var tags: JsonNodeObj
# http://localhost:41184/notes/77ec1bfbaccd4708a9f649f42896f437&token=e5f6644fbf6a97ddc55648dae72b11caecda6c6642d8ce0d3b20129b89b196385737eb908923542c3343649ebbf865b55bda031ab4c3a16edc7723ef2ad77d8f
# http://localhost:41184/notes/77ec1bfbaccd4708a9f649f42896f437?fields=id,parent_id,title,body,created_time,updated_time,is_conflict,latitude,longitude,altitude,author,source_url,is_todo,todo_due,todo_completed,source,source_application,application_data,order,user_created_time,user_updated_time,encryption_cipher_text,encryption_applied,markup_language,is_shared,share_id,conflict_original_id,master_key_id&token=e5f6644fbf6a97ddc55648dae72b11caecda6c6642d8ce0d3b20129b89b196385737eb908923542c3343649ebbf865b55bda031ab4c3a16edc7723ef2ad77d8f
2022-08-06 16:51:49 -04:00
# # ##
# # END TESTS SECTION ##
# # ##