From 45a6522e2f6fd46f0f8c376748d539c0e4511490 Mon Sep 17 00:00:00 2001 From: bruno Date: Thu, 10 Nov 2022 15:55:58 -0500 Subject: [PATCH] ajout des fichiers du projet TheNewJoplin --- README.md | 92 ++-- outils/base/OutilsBase.nim | 0 outils/commun/OutilsCommun.nim | 0 outils/commun/password_utils.nim | 56 ++ outils/commun/utils.nim | 22 + .../joplin/joplin_json_utils.nim | 24 + outils/communication/joplin/joplin_utils.nim | 516 ++++++++++++++++++ outils/db/database_utils.nim | 132 +++++ outils/web/web_utils.nim | 113 ++++ 9 files changed, 924 insertions(+), 31 deletions(-) create mode 100644 outils/base/OutilsBase.nim create mode 100644 outils/commun/OutilsCommun.nim create mode 100644 outils/commun/password_utils.nim create mode 100644 outils/commun/utils.nim create mode 100644 outils/communication/joplin/joplin_json_utils.nim create mode 100644 outils/communication/joplin/joplin_utils.nim create mode 100644 outils/db/database_utils.nim create mode 100644 outils/web/web_utils.nim diff --git a/README.md b/README.md index 0a80ea8..a20f205 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,8 @@ *** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use. *** https://www.markdownguide.org/basic-syntax/#reference-style-links --> -[![Contributors][contributors-shield]][contributors-url] -[![Forks][forks-shield]][forks-url] -[![Stargazers][stars-shield]][stars-url] -[![Issues][issues-shield]][issues-url] -[![MIT License][license-shield]][license-url] +[![GNU License][license-shield]][license-url] +[![Nim][Nim.com]][Nim-url] [![LinkedIn][linkedin-shield]][linkedin-url] @@ -70,6 +67,11 @@
  • Usage
  • +
  • Base
  • +
  • Usage
  • +
  • Usage
  • +
  • Usage
  • +
  • Usage
  • Roadmap
  • Contributing
  • License
  • @@ -78,41 +80,63 @@ - - ## About The Project -[![Product Name Screen Shot][product-screenshot]](https://example.com) +[![Product Name Screen Shot][product-screenshot]](https://git.bcmaison.cf/Projets/nim-lib/src/branch/main/images/screenshot.png) -There are many great README templates available on GitHub; however, I didn't find one that really suited my needs so I created this enhanced one. I want to create a README template so amazing that it'll be the last one you ever need -- I think this is it. +Cette librairie permet de centraliser tous les types de besoins génériques à une même place. Elle est découpé en plusieurs regroupement : -Here's why: -* Your time should be focused on creating something amazing. A project that solves a problem and helps others -* You shouldn't be doing the same tasks over and over like creating a README from scratch -* You should implement DRY principles to the rest of your life :smile: -Of course, no one template will serve all projects since your needs may be different. So I'll be adding more in the near future. You may also suggest changes by forking this repo and creating a pull request or opening an issue. Thanks to all the people have contributed to expanding this template! +| Section | script | description | +| -------------------- | ----------------------- | ---------------------------------------------------------------------------- | +| 📂 base | 📄 OutilsBase.nim | Fonctions de base, petit traitement et opérations sur chaines, encryption. | +| 📂 commun | 📄 OutilsCommun.nim | Traitement et fonctions communes sur heures, date, os, fichiers, répertoires | +| 📂 commun | 📄 password_utils.nim | | +| 📂 commun | 📄 utils.nim | | +| 📂 commun ➡️ load | 📄 | Fonctions de chargement des données de divers types (CSV, list, json, ...) | +| 📂 commun ➡️ save | 📄 | Fonctions de sauvegarde des données de divers types (CSV, list, json, ...) | +| 📂 Communication | 📄 | Fonctions de communications | +| 📂 Comm. ➡️ discord | 📄 | communication de type discord | +| 📂 Comm. ➡️ email | 📄 | communication de type email | +| 📂 Comm. ➡️ server | 📄 | communication de type serveur | +| 📂 Comm. ➡️ Joplin | 📄 joplin_json_utils.nim | communication de type Joplin | +| 📂 Comm. ➡️ Joplin | 📄 joplin_utils.nim | communication de type Joplin | +| 📂 db | 📄 database_utils.nim | Fonctions de type base de données | +| 📂 listes | 📄 | Fonctions de traitement de listes | +| 📂 windows ➡️ registry | 📄 | (Windows) Fonctions de traitement sur clé de registre | +| 📂 windows ➡️ wmi | 📄 | (Windows) Fonctions de traitement sur WMI | +| 📂 web | 📄 web_utils.nim | Fonctions de type web | -Use the `BLANK_README.md` to get started.

    (back to top)

    - - ### Built With -This section should list any major frameworks/libraries used to bootstrap your project. Leave any add-ons/plugins for the acknowledgements section. Here are a few examples. +Ces fonctions sont fait pour des projets développés en Nim. Voici la liste des modules a importer par script. -* [![Next][Next.js]][Next-url] -* [![React][React.js]][React-url] -* [![Vue][Vue.js]][Vue-url] -* [![Angular][Angular.io]][Angular-url] -* [![Svelte][Svelte.dev]][Svelte-url] -* [![Laravel][Laravel.com]][Laravel-url] -* [![Bootstrap][Bootstrap.com]][Bootstrap-url] -* [![JQuery][JQuery.com]][JQuery-url] -* [![Nim][Nim.com]][Nim-url] +| Section | script | modules | +| -------------------- | ----------------------- | -------------------------------- | +| 📂 base | 📄 OutilsBase.nim | | +| 📂 commun | 📄 OutilsCommun.nim | | +| 📂 commun | 📄 password_utils.nim | md5, bcrypt, random | +| 📂 commun | 📄 utils.nim | std/times | +| 📂 commun ➡️ load | | | +| 📂 commun ➡️ save | | | +| 📂 Communication | | | +| 📂 Comm. ➡️ discord | | | +| 📂 Comm. ➡️ email | | | +| 📂 Comm. ➡️ server | | | +| 📂 Comm. ➡️ Joplin | 📄 joplin_json_utils.nim | | +| 📂 Comm. ➡️ Joplin | 📄 joplin_utils.nim | | +| 📂 db | 📄 database_utils.nim | os, logging, parsecfg, db_sqlite | +| 📂 listes | 📄 listes_utils.nim | | +| 📂 windows ➡️ registry | 📄 registry_utils.nim | | +| 📂 windows ➡️ wmi | 📄 wmi_utils.nim | | +| 📂 web | 📄 web_utils.nim | | + + +[![Nim][Nim.com]][Nim-url]

    (back to top)

    @@ -163,6 +187,12 @@ _For more examples, please refer to the [Documentation](https://example.com)_

    (back to top)

    + +## Base + +Détails, spécifications spécial sur la section Base + +

    (back to top)

    @@ -245,13 +275,13 @@ Use this space to list resources you find helpful and would like to give credit [forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=for-the-badge [forks-url]: https://github.com/othneildrew/Best-README-Template/network/members [stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=for-the-badge -[stars-url]: https://github.com/othneildrew/Best-README-Template/stargazers +[stars-url]: https://git.bcmaison.cf/Projets/nim-lib/stars [issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=for-the-badge [issues-url]: https://github.com/othneildrew/Best-README-Template/issues -[license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=for-the-badge -[license-url]: https://github.com/othneildrew/Best-README-Template/blob/master/LICENSE.txt +[license-shield]: https://img.shields.io/badge/LICENSE-GNU-green?style=for-the-badge +[license-url]: https://git.bcmaison.cf/Projets/nim-lib/src/branch/main/LICENSE.txt [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 -[linkedin-url]: https://linkedin.com/in/othneildrew +[linkedin-url]: https://www.linkedin.com/in/bruno-charest-a3a6158/ [product-screenshot]: images/screenshot.png [Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white [Next-url]: https://nextjs.org/ diff --git a/outils/base/OutilsBase.nim b/outils/base/OutilsBase.nim new file mode 100644 index 0000000..e69de29 diff --git a/outils/commun/OutilsCommun.nim b/outils/commun/OutilsCommun.nim new file mode 100644 index 0000000..e69de29 diff --git a/outils/commun/password_utils.nim b/outils/commun/password_utils.nim new file mode 100644 index 0000000..16b1bd8 --- /dev/null +++ b/outils/commun/password_utils.nim @@ -0,0 +1,56 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-08-09 +# +# __ DESCRIPTIONS __ +# password_utils : procedure related password +# +# Inspiration of : https://ttj.dk/blog/2019/01/20/setup-a-website-with-nim +# Copyright 2019 - Thomas T. Jarløv +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +import md5 +import bcrypt +import random +randomize() + +# --==--==--==--==--==--==--==--==--==--==-- # +# Let and VAR : for random +# --==--==--==--==--==--==--==--==--==--==-- # +var urandom: File +let useUrandom = urandom.open("/dev/urandom") + + +# --==--==--==--==--==--==--==--==--==--==--=--==--==--=--==--==--=--==--==-- # +# PROC : Make Salt +## : Generate random salt. Uses cryptographically secure /dev/urandom +## : on platforms where it is available, and Nim's random module in other cases. +# --==--==--==--==--==--==--==--==--==--==--=--==--==--=--==--==--=--==--==-- # +proc makeSalt*(): string = + result = "" + if useUrandom: + var randomBytes: array[0..127, char] + discard urandom.readBuffer(addr(randomBytes), 128) + for ch in randomBytes: + if ord(ch) in {32..126}: + result.add(ch) + else: + for i in 0..127: + result.add(chr(rand(94) + 32)) # Generate numbers from 32 to 94 + 32 = 126 + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : Make session key +## Creates a random key to be used to authorize a session. +# --==--==--==--==--==--==--==--==--==--==-- # +proc makeSessionKey*(): string = + let random = makeSalt() + return bcrypt.hash(random, genSalt(8)) + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : make password +## Creates an MD5 hash by combining password and salt +# --==--==--==--==--==--==--==--==--==--==-- # +proc makePassword*(password, salt: string, comparingTo = ""): string = + let bcryptSalt = if comparingTo != "": comparingTo else: genSalt(8) + result = hash(getMD5(salt & getMD5(password)), bcryptSalt) diff --git a/outils/commun/utils.nim b/outils/commun/utils.nim new file mode 100644 index 0000000..cc161da --- /dev/null +++ b/outils/commun/utils.nim @@ -0,0 +1,22 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-10-25 +# +# __ DESCRIPTIONS __ +# utils : procedures generals utils +# +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # + +import std/times + +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# PROC : Convert epoch millisecond time to human readable time +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +proc convertEpochToHumanTime*(epochTime: int): string = + var humanTime: Time + var dateTime: string + humanTime = fromUnix(convert(Milliseconds, Seconds, epochTime)) + dateTime = format(humanTime, "yyyy-MM-dd' 'HH:mm") + # echo dateTime + return dateTime + diff --git a/outils/communication/joplin/joplin_json_utils.nim b/outils/communication/joplin/joplin_json_utils.nim new file mode 100644 index 0000000..b850a26 --- /dev/null +++ b/outils/communication/joplin/joplin_json_utils.nim @@ -0,0 +1,24 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-08-09 +# +# __ DESCRIPTIONS __ +# joplin_json_utils : Types and procedure related to json request +# +# [TODO] joplin_json_utils : need to create many procedures +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Data structure for TAGS +# --==--==--==--==--==--==--==--==--==--==-- # +type Tag = object + id : string + parent_id : string + title: string + +type Tags = object + items: seq[Tag] + has_more: string + + + diff --git a/outils/communication/joplin/joplin_utils.nim b/outils/communication/joplin/joplin_utils.nim new file mode 100644 index 0000000..661fb1f --- /dev/null +++ b/outils/communication/joplin/joplin_utils.nim @@ -0,0 +1,516 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-08-09 +# +# __ DESCRIPTIONS __ +# joplin_utils : procedure related joplin application +# +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +import os +import net +import times +import osproc +import jester +import std/json +import httpcore +import strutils +import strformat +import std/options +import std/httpclient +import std/asyncdispatch + +import ../../commun/utils # Joplin utils procedures and types + +# from os import sleep +from posix import read, write, fdatasync, close + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup joplin_ping data +# --==--==--==--==--==--==--==--==--==--==-- # +type + joplin_ping* = object + ping_result*: seq[string] + ping_status*: bool + req*: Request + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup joplin_tags data +# --==--==--==--==--==--==--==--==--==--==-- # +type + joplin_tags* = object + id*, parent_id*, title*: seq[string] + created_time*, updated_time*, user_created_time*, user_updated_time*, + is_shared*: seq[int] + req*: Request + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup joplin_notebooks data +# --==--==--==--==--==--==--==--==--==--==-- # +type + joplin_notebooks* = object + id*, parent_id*, title*, share_id*, icon*: seq[string] + created_time*, updated_time*, user_created_time*, user_updated_time*, + is_shared*: seq[int] + req*: Request + + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup joplin_notes data +# --==--==--==--==--==--==--==--==--==--==-- # +type + joplin_notes* = object + id*, parent_id*, title*, body*, author*, source_url*, source*, + source_application*, application_data*, share_id*, + conflict_original_id*, body_html*, base_url*: seq[string] + updated_time*, is_conflict*, is_todo*, todo_due*, + todo_completed*, user_created_time*, user_updated_time*, + markup_language*, is_shared*: seq[int] + latitude*, longitude*, altitude*, order*: seq[float] + created_time*: seq[string] + req*: Request + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup joplin_note data +# --==--==--==--==--==--==--==--==--==--==-- # +type + joplin_note* = object + id*, parent_id*, title*, body*, author*, source_url*, source*, + source_application*, application_data*, conflict_original_id*, + master_key_id*, share_id*, encryption_cipher_text * : string + created_time*, updated_time*, user_created_time*, user_updated_time*, + is_conflict*, is_todo*, todo_due*, todo_completed*, markup_language*, + is_shared*, encryption_applied*: int + latitude*, longitude*, altitude*, order*: float + req*: Request + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Setup process data structure +# --==--==--==--==--==--==--==--==--==--==-- # +type + program* = object + pid*: int + stdout*: string + stderr*: string + +# --==--==--==--==--==--==--==--==--==--==-- # +# DURATION : duration variables +# --==--==--==--==--==--==--==--==--==--==-- # +let + resetDuration = initDuration(seconds = 2) + deciSecondDuration* = initDuration(milliseconds = 100) + qtrsecondDuration* = initDuration(milliseconds = 250) + +# --==--==--==--==--==--==--==--==--==--==-- # +# VAR : connexion http variables +# --==--==--==--==--==--==--==--==--==--==-- # +var + client = newHttpClient() + lastConnection = getTime().utc + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : reset HTTP client +# --==--==--==--==--==--==--==--==--==--==-- # +proc resetHttpClient() = + if (getTime().utc - lastConnection) > resetDuration: + # Probably a new timeout. We have not yet experienced a long outage. + # We may however be entering an extended outage. + # Creating the new clients seems to use up lots of CPU. + # I want to do that as little as possible. + try: + client.close() + except: + echo("Attempted to close clients. Probably do not exist.") + echo("Current exception: ", getCurrentExceptionMsg()) + client = newHttpClient(timeout = 500) + +# --==--==--==--==--==--==--==--==--==--==--==--==--==-==--==--==- # +# PROC : use the joplin API ping to validate service availibility +# --==--==--==--==--==--==--==--==--==--==--==--==--==-==--==--==- # +proc ping_joplin*(token: string): Future[joplin_ping] {.async.} = + + var j_p: joplin_ping + var client = newAsyncHttpClient() + var url: string + j_p.ping_status = false + try: + url = fmt"http://localhost:41184/ping?token={token}" + var json = await client.getContent(url) + j_p.ping_result.add(json) + + echo j_p.ping_result[0] + + if j_p.ping_result[0] == "JoplinClipperServer": + j_p.ping_status = true + + except TimeoutError, IOError, OSError: + # I naively think I would see this thrown or the plain except below. + # But I almost never see an Error raised. + echo("Current Exception: ", getCurrentException().name) + echo("Current Exception Msg: ", getCurrentExceptionMsg()) + echo("Sleeping for 1 seconds at: ", getTime().utc) + sleep(500) + resetHttpClient() + j_p.ping_status = false + except: + echo("Current Exception: ", getCurrentException().name) + echo("Current Exception Msg: ", getCurrentExceptionMsg()) + echo("Sleeping for 1 seconds at: ", getTime().utc) + j_p.ping_status = false + return j_p + # parse jason + #let joplin_notes_Json = parseJson(json) + + + # --==--==--==--==--==--==--==--==--==--==-- # + # PROC : get all joplin NOTEBOOKS + # --==--==--==--==--==--==--==--==--==--==-- # +proc get_joplin_notebooks*(token: string): Future[joplin_notebooks] {.async.} = + + # Variables + var j_nb: joplin_notebooks + var has_more: bool = true + var page: int = 1 + var url: string + var client = newAsyncHttpClient() + var pingCheck: joplin_ping + + pingCheck = waitFor ping_joplin(token) + + # check the joplin status befor query + if pingCheck.ping_status: + + # make sure to check all pages + while has_more == true: + + # request joplin API for notebooks + url = fmt"http://localhost:41184/folders?page={page}&token={token}" + echo("URL notebooks : ", url) + var json = await client.getContent(url) + + # parse jason + let joplin_notebooks_Json = parseJson(json) + + # valider qu'il n'y a plus de page + if not joplin_notebooks_Json["has_more"].getBool: + has_more = false + + # store json info into an object + var count: int = 1 + for nb in joplin_notebooks_Json["items"]: + j_nb.id.add(nb["id"].getstr) + j_nb.parent_id.add(nb["parent_id"].getstr) + j_nb.title.add(nb["title"].getstr) + count += 1 + + # aller à la page suivante + page += 1 + + return j_nb + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : get all joplin NOTES +# --==--==--==--==--==--==--==--==--==--==-- # +proc get_joplin_notes*(token: string): Future[joplin_notes] {.async.} = + + # Variables + var j_notes: joplin_notes + var has_more: bool = true + var page: int = 1 + var url: string + var client = newAsyncHttpClient() + var pingCheck: joplin_ping + pingCheck = waitFor ping_joplin(token) + + # check the joplin status befor query + if pingCheck.ping_status: + + # make sure to check all pages + while has_more == true: + + # request joplin API for Notes + url = fmt"http://localhost:41184/notes?page={page}&fields=id,parent_id,title,created_time,updated_time&token={token}" + echo("URL notes : ", url) + var json = await client.getContent(url) + + # parse jason + let joplin_notes_Json = parseJson(json) + + # valider qu'il n'y a plus de page + if not joplin_notes_Json["has_more"].getBool: + has_more = false + + #echo joplin_notes_Json + + # store json info into an object + var count: int = 1 + var epochTime: int + var humanTime: Time + for nb in joplin_notes_Json["items"]: + j_notes.id.add(nb["id"].getstr) + j_notes.parent_id.add(nb["parent_id"].getstr) + j_notes.title.add(nb["title"].getstr) + #epochTime = nb["created_time"].getInt + #humanTime = fromUnix(convert(Milliseconds, Seconds, epochTime)) + + j_notes.created_time.add(convertEpochToHumanTime(nb[ + "created_time"].getInt)) + count += 1 + + # aller à la page suivante + page += 1 + + return j_notes + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : get joplin NOTE +# --==--==--==--==--==--==--==--==--==--==-- # +proc get_joplin_note*(token: string, noteid: string): Future[ + joplin_note] {.async.} = + + # Variables + var j_note: joplin_note + var has_more: bool = true + var page: int = 1 + var url: string + var client = newAsyncHttpClient() + + # Check joplin connection availibility + var pingCheck: joplin_ping + pingCheck = waitFor ping_joplin(token) + + + # Query for a selected note + url = fmt"http://localhost:41184/notes/{noteid}?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={token}" + echo("URL notes : ", url) + var jsonNote = await client.getContent(url) + + # parse jason + let joplin_note_Json = parseJson(jsonNote) + + # get all informations for a selected note + j_note.id = joplin_note_Json["id"].getstr + j_note.parent_id = joplin_note_Json["parent_id"].getstr + j_note.title = joplin_note_Json["title"].getstr + j_note.body = joplin_note_Json["body"].getstr + j_note.author = joplin_note_Json["author"].getstr + j_note.source_url = joplin_note_Json["source_url"].getstr + j_note.source = joplin_note_Json["source"].getstr + j_note.source_application = joplin_note_Json["source_application"].getstr + j_note.application_data = joplin_note_Json["application_data"].getstr + j_note.share_id = joplin_note_Json["share_id"].getstr + j_note.conflict_original_id = joplin_note_Json["conflict_original_id"].getstr + j_note.created_time = joplin_note_Json["created_time"].getInt + j_note.updated_time = joplin_note_Json["updated_time"].getInt + j_note.is_conflict = joplin_note_Json["is_conflict"].getInt + j_note.latitude = joplin_note_Json["latitude"].getFloat + j_note.longitude = joplin_note_Json["longitude"].getFloat + j_note.altitude = joplin_note_Json["altitude"].getFloat + j_note.is_todo = joplin_note_Json["is_todo"].getInt + j_note.todo_due = joplin_note_Json["todo_due"].getInt + j_note.todo_completed = joplin_note_Json["todo_completed"].getInt + j_note.order = joplin_note_Json["order"].getFloat + j_note.user_created_time = joplin_note_Json["user_created_time"].getInt + j_note.user_updated_time = joplin_note_Json["user_updated_time"].getInt + j_note.encryption_cipher_text = joplin_note_Json[ + "encryption_cipher_text"].getstr + j_note.encryption_applied = joplin_note_Json["encryption_applied"].getInt + j_note.markup_language = joplin_note_Json["markup_language"].getInt + j_note.is_shared = joplin_note_Json["is_shared"].getInt + j_note.master_key_id = joplin_note_Json["master_key_id"].getstr + + + echo j_note + return j_note + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : get all joplin TAGS +# --==--==--==--==--==--==--==--==--==--==-- # +proc get_joplin_tags*(token: string): Future[joplin_tags] {.async.} = + + # Variables + var j_tags: joplin_tags + var has_more: bool = true + var page: int = 1 + var url: string + var client = newAsyncHttpClient() + var pingCheck: joplin_ping + pingCheck = waitFor ping_joplin(token) + + # check the joplin status befor query + if pingCheck.ping_status: + + # make sure to check all pages + while has_more == true: + + # request joplin API for notebooks + url = fmt"http://localhost:41184/tags?page={page}&token={token}" + echo("URL tags : ", url) + var json = await client.getContent(url) + + # parse jason + let joplin_tags_Json = parseJson(json) + + # valider qu'il n'y a plus de page + if not joplin_tags_Json["has_more"].getBool: + has_more = false + + # store json info into an object + var count: int = 1 + for nb in joplin_tags_Json["items"]: + j_tags.id.add(nb["id"].getstr) + j_tags.parent_id.add(nb["parent_id"].getstr) + j_tags.title.add(nb["title"].getstr) + count += 1 + + # aller à la page suivante + page += 1 + + return j_tags + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : get the token from Joplin Terminal +# --==--==--==--==--==--==--==--==--==--==-- # +proc get_joplin_cli_token*(): string = + var flagName: string = "" + var flagValue: string = "" + var result = execCmdEx("joplin config api.token") + + if result.exitCode == 0: + let param1 = result.output + let flagSplit = param1.split(" = ") + flagName = flagSplit[0] + flagValue = flagSplit[1] + + return flagValue + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : launch any program and get PID +# --==--==--==--==--==--==--==--==--==--==-- # +proc launchProgram(app: string = "", workingPath: string = "", arguments: array[ + 2, string]): int {.thread.} = + + var p = startProcess(joinPath(workingPath, app), workingPath, arguments) + let pid = p.processID() + var outhdl = outputHandle(p) + var inputhdl = inputHandle(p) + var errhdl = errorHandle(p) + return pid + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : Start joplin cli +# --==--==--==--==--==--==--==--==--==--==-- # +proc joplin_cli_start*(): int {.thread.} = + + let joplinProcessId = launchProgram("joplin", "/usr/bin/", ["server", "start"]) + return joplinProcessId + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : stop Joplin Terminal +# --==--==--==--==--==--==--==--==--==--==-- # +proc joplin_cli_stop*(): int {.thread.} = + let joplinProcessId = launchProgram("joplin", "/usr/bin/", ["server", "stop"]) + return joplinProcessId + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : check Joplin Terminal staus +# --==--==--==--==--==--==--==--==--==--==-- # +proc joplin_cli_status*(): bool = + var rc = false + var result = execCmdEx("joplin server status") + + if result.exitCode == 0: + if "Server is not running" in result.output: + echo "Joplin Terminal cli status is down : ", result.output + rc = false + else: + echo "Joplin Terminal cli status is up : ", result.output + rc = true + else: + echo "Error validate joplin terminal status : ", result.output + return rc + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : start or stop Joplin Terminal +# --==--==--==--==--==--==--==--==--==--==-- # +proc joplin_cli_start_stop*(): int = + var isStart: int = 0 + var sleep_delay_frame: int = 50 + var sleep_max: int = 5000 + + if joplin_cli_status() == false: + isStart = joplin_cli_start() + while joplin_cli_status() == false: + sleep(sleep_delay_frame) + echo "Joplin client Terminal started: ", isStart + + else: + echo "Joplin client Terminal is alredy started !" + #if joplin_cli_status() == true: + isStart = joplin_cli_stop() + while joplin_cli_status() == true: + sleep(sleep_delay_frame) + echo "Joplin client Terminal stopped: ", isStart + + return isStart + + + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : +# --==--==--==--==--==--==--==--==--==--==-- # + +# proc get_joplin_tags_json*(token:string): Future[] {.async.} = + +# # url = fmt"http://localhost:41184/notes/{id}?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={token}" + +# # url = fmt"http://localhost:41184/tags/{id}?fields=id,parent_id,title,created_time,updated_time,user_created_time,user_updated_time,is_shared&token={token}" + +# # http://localhost:41184/tags?token=e5f6644fbf6a97ddc55648dae72b11caecda6c6642d8ce0d3b20129b89b196385737eb908923542c3343649ebbf865b55bda031ab4c3a16edc7723ef2ad77d8f + +# # Variables +# var j_tags: joplin_tags +# var has_more: bool = true +# var page: int = 1 +# var url: string +# var client = newAsyncHttpClient() +# var pingCheck: joplin_ping +# pingCheck = waitFor ping_joplin(token) + + +# # check the joplin status befor query +# if pingCheck.ping_status: + +# # make sure to check all pages +# while has_more == true: + +# # request joplin API for notebooks +# url = fmt"http://localhost:41184/tags?page={page}&token={token}" +# echo("URL tags : ", url) +# var json = await client.getContent(url) + +# # parse jason +# let joplin_tags_Json = parseJson(json) + +# # valider qu'il n'y a plus de page +# if not joplin_tags_Json["has_more"].getBool: +# has_more = false + +# # store json info into an object +# var count: int = 1 +# for nb in joplin_tags_Json["items"]: +# echo nb["id"].getstr +# echo nb["parent_id"].getstr +# echo nb["title"].getstr +# count += 1 + +# # aller à la page suivante +# page += 1 + + diff --git a/outils/db/database_utils.nim b/outils/db/database_utils.nim new file mode 100644 index 0000000..f494203 --- /dev/null +++ b/outils/db/database_utils.nim @@ -0,0 +1,132 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-08-09 +# +# __ DESCRIPTIONS __ +# database_utils : procedure related to database +# +# Inspiration of : https://ttj.dk/blog/2019/01/20/setup-a-website-with-nim +# Copyright 2019 - Thomas T. Jarløv +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # + +import os +import logging +import parsecfg +import db_sqlite + +import ../commun/password_utils + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : Generate Database +# --==--==--==--==--==--==--==--==--==--==-- # +proc generateDB*() = + echo "Generating database" + + # Load the connection details + let + dict = loadConfig("config/config.cfg") + db_user = dict.getSectionValue("Database", "user") + db_pass = dict.getSectionValue("Database", "pass") + db_name = dict.getSectionValue("Database", "name") + db_host = dict.getSectionValue("Database", "host") + db_folder = dict.getSectionValue("Database", "folder") + dbexists = if fileExists(db_host): true else: false + + if dbexists: + echo " - Database already exists. Inserting tables if they do not exist." + + # Creating database folder if it doesn't exist + discard existsOrCreateDir(db_folder) + + # Open DB + echo " - Opening database" + var db = open(connection = db_host, user = db_user, password = db_pass, + database = db_name) + + # Person table contains information about the + # registrered users + if not db.tryExec(sql(""" + create table if not exists person( + id integer primary key, + name varchar(60) not null, + password varchar(300) not null, + email varchar(254) not null, + creation timestamp not null default (STRFTIME('%s', 'now')), + modified timestamp not null default (STRFTIME('%s', 'now')), + salt varbin(128) not null, + status varchar(30) not null, + timezone VARCHAR(100), + secretUrl VARCHAR(250), + lastOnline timestamp not null default (STRFTIME('%s', 'now')) + );""")): + echo " - Database: person table already exists" + + # Session table contains information about the users + # cookie ID, IP and last visit + if not db.tryExec(sql(""" + create table if not exists session( + id integer primary key, + ip inet not null, + key varchar(300) not null, + userid integer not null, + lastModified timestamp not null default (STRFTIME('%s', 'now')), + foreign key (userid) references person(id) + );""")): + echo " - Database: session table already exists" + + # Sesion table contains information about the search + # + if not db.tryExec(sql(""" + create table if not exists search( + id integer primary key, + search_title varchar(300) not null, + query varchar(300) not null, + search_type varchar(100) not null, + creation timestamp not null default (STRFTIME('%s', 'now')), + lastModified timestamp not null default (STRFTIME('%s', 'now')), + foreign key (userid) references person(id) + );""")): + echo " - Database: search table already exists" + + +# --==--==--==--==--==--==--==--==--==--==-- # +# PROC : Create Administrator User +# --==--==--==--==--==--==--==--==--==--==-- # +proc createAdminUser*(db: DbConn, args: seq[string]) = + ## Create new admin user + + var iName = "" + var iEmail = "" + var iPwd = "" + + # Loop through all the arguments and get the args + # containing the user information + for arg in args: + if arg.substr(0, 1) == "u:": + iName = arg.substr(2, arg.len()) + elif arg.substr(0, 1) == "p:": + iPwd = arg.substr(2, arg.len()) + elif arg.substr(0, 1) == "e:": + iEmail = arg.substr(2, arg.len()) + + # If the name, password or emails does not exists + # return error + if iName == "" or iPwd == "" or iEmail == "": + error("Missing either name, password or email to create the Admin user.") + + # Generate the password using a salt and hashing. + # Read more about hashing and salting here: + # - https://crackstation.net/hashing-security.htm + # - https://en.wikipedia.org/wiki/Salt_(cryptography) + let salt = makeSalt() + let password = makePassword(iPwd, salt) + + # Insert user into database + if insertID(db, sql"INSERT INTO person (name, email, password, salt, status) VALUES (?, ?, ?, ?, ?)", + $iName, $iEmail, password, salt, "Admin") > 0: + echo "Admin user added" + else: + error("Something went wrong") + + info("Admin added.") diff --git a/outils/web/web_utils.nim b/outils/web/web_utils.nim new file mode 100644 index 0000000..bd4359a --- /dev/null +++ b/outils/web/web_utils.nim @@ -0,0 +1,113 @@ +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# Bruno Charest +# 2022-08-09 +# +# __ DESCRIPTIONS __ +# web_utils : procedure related web interface +# +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +import xlsx # read dashbord.xlsx +import jester +import ../communication/joplin/joplin_utils +import strutils +import std/asyncdispatch + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Selected Option for master web page +# --==--==--==--==--==--==--==--==--==--==-- # +type + selectedOption* = enum + newNote = "New Note", search = "Search", shortcuts = "Shortcuts", + notes = "Notes", notebooks = "Notesbooks", tags = "Tags", + notes_selectednote = "Notes-SelectedNote", dashbord = "Dashboard" + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Dashbord information +# --==--==--==--==--==--==--==--==--==--==-- # +type DashbordData* = object + position*: seq[int] + ext_link*: seq[string] + int_link*: seq[string] + ip_link*: seq[string] + title*: seq[string] + description*: seq[string] + server*: seq[string] + ip*: seq[string] + port*: seq[int] + ssl*: seq[bool] + category*: seq[string] + icon*: seq[string] + open_method*: seq[string] + tag*: seq[seq[string]] + hotkey*: seq[string] + color*: seq[string] + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Data Informations for Left Colomn +# --==--==--==--==--==--==--==--==--==--==-- # +type ColomnLeftData* = ref object of RootObj + j_status*: bool + option*: selectedOption + j_notes*: joplin_notes + j_notes_nb*: int + j_notebooks*: joplin_notebooks + j_notebooks_nb*: int + j_tags*: joplin_tags + j_tags_nb*: int + dashbord*: DashbordData + req*: Request + +# --==--==--==--==--==--==--==--==--==--==-- # +# TYPE : Data Informations for Right Colomn +# --==--==--==--==--==--==--==--==--==--==-- # +type ColomnRightData* = ref object of RootObj + j_status*: bool + option*: selectedOption + j_SelectedNote*: joplin_note + j_notebooksNote*: joplin_notebooks + j_tagsNote*: joplin_tags + req*: Request + + +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +# PROC : get data from dashbord file +# --==--==--==--==--==--==--==--==--==--==--==--==--==--==-- # +proc getDashbordData*(): Future[DashbordData] {.async.} = + + var ddata: DashbordData + var tagslist: seq[string] + + let + data = parseExcel("data/dashbord.xlsx", header = true) + sheetName = "dashbord" + #echo data[sheetName] + + let rows = data[sheetName].toSeq(true) + echo rows.len() + for i in 0..(rows.len()-1): + ddata.position.add(parseInt(rows[i][0])) + ddata.ext_link.add(rows[i][1]) + ddata.int_link.add(rows[i][2]) + ddata.ip_link.add(rows[i][3]) + ddata.title.add(rows[i][4]) + ddata.description.add(rows[i][5]) + ddata.server.add(rows[i][6]) + ddata.ip.add(rows[i][7]) + ddata.port.add(parseInt(rows[i][8])) + if rows[i][9] == "OUI": + ddata.ssl.add(true) + else: + ddata.ssl.add(false) + + ddata.category.add(rows[i][10]) + ddata.icon.add(rows[i][11]) + ddata.open_method.add(rows[i][12]) + + tagslist = rows[i][13].split(',') + + ddata.tag.add(tagslist) + ddata.hotkey.add(rows[i][14]) + ddata.color.add(rows[i][15]) + + echo ddata + return ddata