From 191f70477c6a57c94ecd245f76073c44ab119052 Mon Sep 17 00:00:00 2001 From: azivner Date: Sun, 13 Aug 2017 19:43:33 -0400 Subject: [PATCH] app broken up into individual files/modules --- .pylintrc | 2 +- app.py | 331 ------------------------ config.ini | 4 +- generate-password.py | 3 +- run.sh | 2 +- src/app.py | 89 +++++++ src/expanded_note.py | 10 + src/move_after_note.py | 15 ++ src/move_before_note.py | 15 ++ src/move_to_note.py | 19 ++ src/notes.py | 57 ++++ src/notes_children.py | 64 +++++ src/sql.py | 43 +++ src/sync.py | 62 +++++ {templates => src/templates}/app.html | 32 +-- {templates => src/templates}/login.html | 0 src/tree.py | 30 +++ 17 files changed, 426 insertions(+), 352 deletions(-) delete mode 100644 app.py create mode 100644 src/app.py create mode 100644 src/expanded_note.py create mode 100644 src/move_after_note.py create mode 100644 src/move_before_note.py create mode 100644 src/move_to_note.py create mode 100644 src/notes.py create mode 100644 src/notes_children.py create mode 100644 src/sql.py create mode 100644 src/sync.py rename {templates => src/templates}/app.html (69%) rename {templates => src/templates}/login.html (100%) create mode 100644 src/tree.py diff --git a/.pylintrc b/.pylintrc index 40ba4d091..c9c33e7f0 100644 --- a/.pylintrc +++ b/.pylintrc @@ -50,7 +50,7 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,C0301,C0111,R0201,W0102,C0103 +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,C0301,C0111,R0201,W0102,C0103,C0325 # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/app.py b/app.py deleted file mode 100644 index 1dfb5aea3..000000000 --- a/app.py +++ /dev/null @@ -1,331 +0,0 @@ -import sqlite3 -import base64 -from flask import Flask, request, send_from_directory -from flask_restful import Resource, Api -from flask_cors import CORS -from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user -import time -import math -import random -import string -import configparser -import bcrypt -import requests -import json -import os -import binascii -import hashlib - -from flask import render_template, redirect - -def dict_factory(cursor, row): - d = {} - for idx, col in enumerate(cursor.description): - if isinstance(row[idx], buffer): - d[col[0]] = base64.b64encode(row[idx]) - else: - d[col[0]] = row[idx] - - return d - -conn = sqlite3.connect('demo.ncdb') -conn.row_factory = dict_factory - -app = Flask(__name__) -app.secret_key = 'dshjkjsdhfk9832fsdlhf' - -class User(UserMixin): - pass - -@app.route('/login', methods=['GET']) -def login_form(): - return render_template('login.html') - -@app.route('/app', methods=['GET']) -@login_required -def show_app(): - return render_template('app.html') - -@app.route('/logout', methods=['POST']) -@login_required -def logout(): - logout_user() - return redirect('login') - -config = configparser.ConfigParser() -config.read('config.ini') - -user = User() -user.id = config['Login']['username'] - -hashedPassword = config['Login']['password-hash'].encode('utf-8') - -@app.route('/login', methods=['POST']) -def login_post(): - inputPassword = request.form['password'].encode('utf-8') - - if request.form['username'] == user.id and bcrypt.hashpw(inputPassword, hashedPassword) == hashedPassword: - rememberMe = True if 'remember-me' in request.form else False - - login_user(user, remember=rememberMe) - - return redirect('app') - else: - return render_template('login.html', failedAuth=True) - -CORS(app) - -@app.route('/static/') -def send_static(path): - return send_from_directory('static', path) - -api = Api(app) - -def insert(tablename, rec): - keys = ','.join(rec.keys()) - question_marks = ','.join(list('?'*len(rec))) - values = tuple(rec.values()) - cursor = execute('INSERT INTO '+tablename+' ('+keys+') VALUES ('+question_marks+')', values) - return cursor.lastrowid - -def delete(tablename, note_id): - execute("DELETE FROM " + tablename + " WHERE note_id = ?", [note_id]) - -def execute(sql, params=[]): - cursor = conn.cursor() - cursor.execute(sql, params) - return cursor - -def getResults(sql, params=[]): - cursor = conn.cursor() - query = cursor.execute(sql, params) - return query.fetchall() - -def getSingleResult(sql, params=()): - cursor = conn.cursor() - query = cursor.execute(sql, params) - return query.fetchone() - -class Query(Resource): - def get(self): - sql = request.args.get('sql') - - return getResults(sql) - -api.add_resource(Query, '/query') - -class Notes(Resource): - def get(self, note_id): - return { - 'detail': getSingleResult("select * from notes where note_id = ?", [note_id]), - 'formatting': getResults("select * from formatting where note_id = ? order by note_offset", [note_id]), - 'links': getResults("select * from links where note_id = ? order by note_offset", [note_id]), - 'images': getResults("select * from images where note_id = ? order by note_offset", [note_id]) - } - - def put(self, note_id): - note = request.get_json(force=True) - - now = math.floor(time.time()) - - execute("update notes set note_text = ?, note_title = ?, date_modified = ? where note_id = ?", [note['detail']['note_text'], note['detail']['note_title'], now, note_id]) - - delete("formatting", note_id) - - for fmt in note['formatting']: - insert("formatting", fmt) - - delete("images", note_id) - - for img in note['images']: - img['image_data'] = buffer(base64.b64decode(img['image_data'])) - - insert("images", img) - - delete("links", note_id) - - for link in note['links']: - insert("links", link) - - conn.commit() - - return {} - - def delete(self, note_id): - children = getResults("select note_id from notes_tree where note_pid = ?", [note_id]) - - for child in children: - self.delete(child['note_id']) - - delete("notes_tree", note_id) - delete("notes", note_id) - - conn.commit() - -api.add_resource(Notes, '/notes/') - -class NotesChildren(Resource): - def post(self, parent_note_id): - note = request.get_json(force=True) - - noteId = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(22)) - - if parent_note_id == "root": - parent_note_id = "" - - new_note_pos = 0 - - if note['target'] == 'into': - res = getSingleResult('select max(note_pos) as max_note_pos from notes_tree where note_pid = ?', [parent_note_id]) - max_note_pos = res['max_note_pos'] - - if max_note_pos is None: # no children yet - new_note_pos = 0 - else: - new_note_pos = max_note_pos + 1 - elif note['target'] == 'after': - after_note = getSingleResult('select note_pos from notes_tree where note_id = ?', [note['target_note_id']]) - - new_note_pos = after_note['note_pos'] + 1 - - execute('update notes_tree set note_pos = note_pos + 1 where note_pid = ? and note_pos > ?', [parent_note_id, after_note['note_pos']]) - else: - raise Exception('Unknown target: ' + note['target']) - - now = math.floor(time.time()) - - insert("notes", { - 'note_id': noteId, - 'note_title': note['note_title'], - 'note_text': '', - 'note_clone_id': '', - 'date_created': now, - 'date_modified': now, - 'icon_info': 'pencil', - 'is_finished': 0 - }) - - insert("notes_tree", { - 'note_id': noteId, - 'note_pid': parent_note_id, - 'note_pos': new_note_pos, - 'is_expanded': 0 - }) - - conn.commit() - - return { - 'note_id': noteId - } - -api.add_resource(NotesChildren, '/notes//children') - -class MoveAfterNote(Resource): - def put(self, note_id, after_note_id): - after_note = getSingleResult("select * from notes_tree where note_id = ?", [after_note_id]) - - if after_note <> None: - execute("update notes_tree set note_pos = note_pos + 1 where note_pid = ? and note_pos > ?", [after_note['note_pid'], after_note['note_pos']]) - - execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [after_note['note_pid'], after_note['note_pos'] + 1, note_id]) - - conn.commit() - -api.add_resource(MoveAfterNote, '/notes//moveAfter/') - -class MoveBeforeNote(Resource): - def put(self, note_id, before_note_id): - before_note = getSingleResult("select * from notes_tree where note_id = ?", [before_note_id]) - - if before_note <> None: - execute("update notes_tree set note_pos = note_pos + 1 where note_id = ?", [before_note_id]) - - execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [before_note['note_pid'], before_note['note_pos'], note_id]) - - conn.commit() - -api.add_resource(MoveBeforeNote, '/notes//moveBefore/') - -class MoveToNote(Resource): - def put(self, note_id, parent_id): - res = getSingleResult('select max(note_pos) as max_note_pos from notes_tree where note_pid = ?', [parent_id]) - max_note_pos = res['max_note_pos'] - new_note_pos = 0 - - if max_note_pos is None: # no children yet - new_note_pos = 0 - else: - new_note_pos = max_note_pos + 1 - - execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [parent_id, new_note_pos, note_id]) - - conn.commit() - -api.add_resource(MoveToNote, '/notes//moveTo/') - -class ExpandedNote(Resource): - def put(self, note_id, expanded): - execute("update notes_tree set is_expanded = ? where note_id = ?", [expanded, note_id]) - - conn.commit() - -api.add_resource(ExpandedNote, '/notes//expanded/') - -class Tree(Resource): - def get(self): - notes = getResults("select notes_tree.*, notes.note_title from notes_tree join notes on notes.note_id = notes_tree.note_id order by note_pid, note_pos") - - rootNotes = [] - notesMap = {} - - for note in notes: - note['children'] = [] - - if not note['note_pid']: - rootNotes.append(note) - - notesMap[note['note_id']] = note - - for note in notes: - if note['note_pid'] != "": - parent = notesMap[note['note_pid']] - - parent['children'].append(note) - parent['folder'] = True - - return rootNotes - -api.add_resource(Tree, '/tree') - -login_manager = LoginManager() -login_manager.init_app(app) -login_manager.login_view = 'login_form' - -@login_manager.user_loader -def load_user(user_id): - if user_id == user.id: - return user - else: - return None - -syncServerUrl = config['Sync']['sync-server-url'] -syncServerUsername = config['Sync']['sync-server-username'] -syncServerPassword = config['Sync']['sync-server-password'] - -nonce = binascii.hexlify(bytearray(os.urandom(32))) - -print('Nonce: ' + nonce) - -# SHA256(user + ":" + SHA256(user + ":" + password) + ":" + nonce) where SHA256 is a hex encoded value -auth = hashlib.sha256(syncServerUsername + ":" + hashlib.sha256(syncServerPassword + ":" + syncServerPassword).hexdigest() + ":" + nonce).hexdigest() - -response = requests.post(syncServerUrl + "/login", json={ - 'user': syncServerUsername, - 'nonce': nonce, - 'auth': auth -}) - -print(response) - -if __name__ == '__main__': - app.run(host='0.0.0.0') \ No newline at end of file diff --git a/config.ini b/config.ini index cbda03fc7..5648cafe6 100644 --- a/config.ini +++ b/config.ini @@ -2,9 +2,9 @@ # Enter below credentials with with which you want to authenticate to Notecase web app username=adam # This is bcrypt password hash. You can use generate-password.py (in this directory) to hash your password -password-hash=$2b$12$jcbhRx6WRbCRogpCckH1hehWrHWgFaFYC3u3ebdVURJX36..fdAca +password-hash=$2b$12$4Ia5lYbbpOv3pxxdoDgjUeAJ9z4FhqyEhhX52ra78FH03wPGx8zGu [Sync] -sync-server-url=http://localhost:57201 +sync-server-url=https://localhost:57201 sync-server-username=syncuser sync-server-password=password \ No newline at end of file diff --git a/generate-password.py b/generate-password.py index f47468c96..4a1cfc921 100644 --- a/generate-password.py +++ b/generate-password.py @@ -1,8 +1,9 @@ #!/usr/bin/python -import bcrypt # pip install bcrypt import getpass +import bcrypt # pip install bcrypt + password1 = getpass.getpass() print('Repeat the same password:') diff --git a/run.sh b/run.sh index 3b3999d17..7a6afb26b 100644 --- a/run.sh +++ b/run.sh @@ -1,6 +1,6 @@ #!/bin/sh export FLASK_DEBUG=1 -export FLASK_APP=app.py +export FLASK_APP=src/app.py flask run \ No newline at end of file diff --git a/src/app.py b/src/app.py new file mode 100644 index 000000000..a43ea78f5 --- /dev/null +++ b/src/app.py @@ -0,0 +1,89 @@ +import bcrypt +import configparser +import os +from flask import Flask, request, send_from_directory +from flask import render_template, redirect +from flask_cors import CORS +from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user +from flask_restful import Api +from move_after_note import MoveAfterNote +from move_to_note import MoveToNote +from notes import Notes +from notes_children import NotesChildren + +from expanded_note import ExpandedNote +from move_before_note import MoveBeforeNote +from tree import Tree + +app = Flask(__name__) +app.secret_key = 'dshjkjsdhfk9832fsdlhf' + +class User(UserMixin): + pass + +@app.route('/login', methods=['GET']) +def login_form(): + return render_template('login.html') + +@app.route('/app', methods=['GET']) +@login_required +def show_app(): + return render_template('app.html') + +@app.route('/logout', methods=['POST']) +@login_required +def logout(): + logout_user() + return redirect('login') + +config = configparser.ConfigParser() +config.read('config.ini') + +user = User() +user.id = config['Login']['username'] + +hashedPassword = config['Login']['password-hash'].encode('utf-8') + +@app.route('/login', methods=['POST']) +def login_post(): + inputPassword = request.form['password'].encode('utf-8') + + if request.form['username'] == user.id and bcrypt.hashpw(inputPassword, hashedPassword) == hashedPassword: + rememberMe = True if 'remember-me' in request.form else False + + login_user(user, remember=rememberMe) + + return redirect('app') + else: + return render_template('login.html', failedAuth=True) + +CORS(app) + +@app.route('/stat/') +def send_stc(path): + return send_from_directory(os.path.join(os.getcwd(), 'static'), path) + +api = Api(app) + +api.add_resource(NotesChildren, '/notes//children') +api.add_resource(MoveAfterNote, '/notes//moveAfter/') +api.add_resource(MoveBeforeNote, '/notes//moveBefore/') +api.add_resource(MoveToNote, '/notes//moveTo/') +api.add_resource(ExpandedNote, '/notes//expanded/') +api.add_resource(Tree, '/tree') + +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = 'login_form' + +@login_manager.user_loader +def load_user(user_id): + if user_id == user.id: + return user + else: + return None + +api.add_resource(Notes, '/notes/') + +if __name__ == '__main__': + app.run(host='0.0.0.0') \ No newline at end of file diff --git a/src/expanded_note.py b/src/expanded_note.py new file mode 100644 index 000000000..0c620b635 --- /dev/null +++ b/src/expanded_note.py @@ -0,0 +1,10 @@ +from flask_restful import Resource + +from sql import execute, commit + + +class ExpandedNote(Resource): + def put(self, note_id, expanded): + execute("update notes_tree set is_expanded = ? where note_id = ?", [expanded, note_id]) + + commit() diff --git a/src/move_after_note.py b/src/move_after_note.py new file mode 100644 index 000000000..383073dcc --- /dev/null +++ b/src/move_after_note.py @@ -0,0 +1,15 @@ +from flask_restful import Resource + +from sql import execute, getSingleResult, commit + + +class MoveAfterNote(Resource): + def put(self, note_id, after_note_id): + after_note = getSingleResult("select * from notes_tree where note_id = ?", [after_note_id]) + + if after_note <> None: + execute("update notes_tree set note_pos = note_pos + 1 where note_pid = ? and note_pos > ?", [after_note['note_pid'], after_note['note_pos']]) + + execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [after_note['note_pid'], after_note['note_pos'] + 1, note_id]) + + commit() diff --git a/src/move_before_note.py b/src/move_before_note.py new file mode 100644 index 000000000..c024bdf44 --- /dev/null +++ b/src/move_before_note.py @@ -0,0 +1,15 @@ +from flask_restful import Resource + +from sql import execute, getSingleResult, commit + + +class MoveBeforeNote(Resource): + def put(self, note_id, before_note_id): + before_note = getSingleResult("select * from notes_tree where note_id = ?", [before_note_id]) + + if before_note <> None: + execute("update notes_tree set note_pos = note_pos + 1 where note_id = ?", [before_note_id]) + + execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [before_note['note_pid'], before_note['note_pos'], note_id]) + + commit() diff --git a/src/move_to_note.py b/src/move_to_note.py new file mode 100644 index 000000000..91e6af176 --- /dev/null +++ b/src/move_to_note.py @@ -0,0 +1,19 @@ +from flask_restful import Resource + +from sql import execute, getSingleResult, commit + + +class MoveToNote(Resource): + def put(self, note_id, parent_id): + res = getSingleResult('select max(note_pos) as max_note_pos from notes_tree where note_pid = ?', [parent_id]) + max_note_pos = res['max_note_pos'] + new_note_pos = 0 + + if max_note_pos is None: # no children yet + new_note_pos = 0 + else: + new_note_pos = max_note_pos + 1 + + execute("update notes_tree set note_pid = ?, note_pos = ? where note_id = ?", [parent_id, new_note_pos, note_id]) + + commit() \ No newline at end of file diff --git a/src/notes.py b/src/notes.py new file mode 100644 index 000000000..fe0050601 --- /dev/null +++ b/src/notes.py @@ -0,0 +1,57 @@ +import base64 +import math +import time + +from flask import request +from flask_restful import Resource + +from sql import delete, execute, insert, getResults, getSingleResult, commit + + +class Notes(Resource): + def get(self, note_id): + return { + 'detail': getSingleResult("select * from notes where note_id = ?", [note_id]), + 'formatting': getResults("select * from formatting where note_id = ? order by note_offset", [note_id]), + 'links': getResults("select * from links where note_id = ? order by note_offset", [note_id]), + 'images': getResults("select * from images where note_id = ? order by note_offset", [note_id]) + } + + def put(self, note_id): + note = request.get_json(force=True) + + now = math.floor(time.time()) + + execute("update notes set note_text = ?, note_title = ?, date_modified = ? where note_id = ?", [note['detail']['note_text'], note['detail']['note_title'], now, note_id]) + + delete("formatting", note_id) + + for fmt in note['formatting']: + insert("formatting", fmt) + + delete("images", note_id) + + for img in note['images']: + img['image_data'] = buffer(base64.b64decode(img['image_data'])) + + insert("images", img) + + delete("links", note_id) + + for link in note['links']: + insert("links", link) + + commit() + + return {} + + def delete(self, note_id): + children = getResults("select note_id from notes_tree where note_pid = ?", [note_id]) + + for child in children: + self.delete(child['note_id']) + + delete("notes_tree", note_id) + delete("notes", note_id) + + commit() diff --git a/src/notes_children.py b/src/notes_children.py new file mode 100644 index 000000000..f3f790890 --- /dev/null +++ b/src/notes_children.py @@ -0,0 +1,64 @@ +import math +import random +import string +import time + +from flask import request +from flask_restful import Resource + +from sql import execute, insert, getSingleResult, commit + + +class NotesChildren(Resource): + def post(self, parent_note_id): + note = request.get_json(force=True) + + noteId = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(22)) + + if parent_note_id == "root": + parent_note_id = "" + + new_note_pos = 0 + + if note['target'] == 'into': + res = getSingleResult('select max(note_pos) as max_note_pos from notes_tree where note_pid = ?', [parent_note_id]) + max_note_pos = res['max_note_pos'] + + if max_note_pos is None: # no children yet + new_note_pos = 0 + else: + new_note_pos = max_note_pos + 1 + elif note['target'] == 'after': + after_note = getSingleResult('select note_pos from notes_tree where note_id = ?', [note['target_note_id']]) + + new_note_pos = after_note['note_pos'] + 1 + + execute('update notes_tree set note_pos = note_pos + 1 where note_pid = ? and note_pos > ?', [parent_note_id, after_note['note_pos']]) + else: + raise Exception('Unknown target: ' + note['target']) + + now = math.floor(time.time()) + + insert("notes", { + 'note_id': noteId, + 'note_title': note['note_title'], + 'note_text': '', + 'note_clone_id': '', + 'date_created': now, + 'date_modified': now, + 'icon_info': 'pencil', + 'is_finished': 0 + }) + + insert("notes_tree", { + 'note_id': noteId, + 'note_pid': parent_note_id, + 'note_pos': new_note_pos, + 'is_expanded': 0 + }) + + commit() + + return { + 'note_id': noteId + } diff --git a/src/sql.py b/src/sql.py new file mode 100644 index 000000000..c9f56f440 --- /dev/null +++ b/src/sql.py @@ -0,0 +1,43 @@ +import sqlite3 +import base64 + +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + if isinstance(row[idx], buffer): + d[col[0]] = base64.b64encode(row[idx]) + else: + d[col[0]] = row[idx] + + return d + +conn = sqlite3.connect('demo.ncdb') +conn.row_factory = dict_factory + +def insert(tablename, rec): + keys = ','.join(rec.keys()) + question_marks = ','.join(list('?'*len(rec))) + values = tuple(rec.values()) + cursor = execute('INSERT INTO '+tablename+' ('+keys+') VALUES ('+question_marks+')', values) + return cursor.lastrowid + +def delete(tablename, note_id): + execute("DELETE FROM " + tablename + " WHERE note_id = ?", [note_id]) + +def execute(sql, params=[]): + cursor = conn.cursor() + cursor.execute(sql, params) + return cursor + +def getResults(sql, params=[]): + cursor = conn.cursor() + query = cursor.execute(sql, params) + return query.fetchall() + +def getSingleResult(sql, params=()): + cursor = conn.cursor() + query = cursor.execute(sql, params) + return query.fetchone() + +def commit(): + conn.commit() diff --git a/src/sync.py b/src/sync.py new file mode 100644 index 000000000..186f02ad1 --- /dev/null +++ b/src/sync.py @@ -0,0 +1,62 @@ +import binascii +import hashlib +import os + +import configparser +import requests + +config = configparser.ConfigParser() +config.read('config.ini') + +syncServerUrl = config['Sync']['sync-server-url'] +syncServerUsername = config['Sync']['sync-server-username'] +syncServerPassword = config['Sync']['sync-server-password'] + +nonce = binascii.hexlify(bytearray(os.urandom(32))) + +print('Nonce: ' + nonce) + +authContent = syncServerUsername + ":" + hashlib.sha256(syncServerUsername + ":" + syncServerPassword).hexdigest() + ":" + nonce + +print('Auth content: ' + authContent) + +# SHA256(user + ":" + SHA256(user + ":" + password) + ":" + nonce) where SHA256 is a hex encoded value +auth = hashlib.sha256(authContent).hexdigest() + +response = requests.post(syncServerUrl + "/login", json={ + 'user': syncServerUsername, + 'nonce': nonce, + 'auth': auth, + 'protocol': '2' +}, verify=False) + +# verify='/home/adam/.notecase/server.pem' + +def printResp(resp): + print('Status: ' + str(resp.status_code)) + + for key in response.headers: + print(key + ': ' + resp.headers[key]) + + print('Body: ' + resp.content) + +printResp(response) + +session = response.headers['Auth'] + +response = requests.get(syncServerUrl + "/document/list", headers={ + 'Auth': session +}, verify=False) + +printResp(response) + +response = requests.post(syncServerUrl + "/document/tree", headers={ + 'Auth': session +}, +json={ + 'id': 1, + 'password': '' +}, +verify=False) + +printResp(response) diff --git a/templates/app.html b/src/templates/app.html similarity index 69% rename from templates/app.html rename to src/templates/app.html index 1667f158d..6bbe54bc0 100644 --- a/templates/app.html +++ b/src/templates/app.html @@ -49,31 +49,31 @@ - - + + - - + + - - + + - - + + - - + + - + - - - - - + + + + + \ No newline at end of file diff --git a/templates/login.html b/src/templates/login.html similarity index 100% rename from templates/login.html rename to src/templates/login.html diff --git a/src/tree.py b/src/tree.py new file mode 100644 index 000000000..c38066327 --- /dev/null +++ b/src/tree.py @@ -0,0 +1,30 @@ +from flask_restful import Resource +from sql import getResults +from flask_restful import Resource + +from sql import getResults + + +class Tree(Resource): + def get(self): + notes = getResults("select notes_tree.*, notes.note_title from notes_tree join notes on notes.note_id = notes_tree.note_id order by note_pid, note_pos") + + rootNotes = [] + notesMap = {} + + for note in notes: + note['children'] = [] + + if not note['note_pid']: + rootNotes.append(note) + + notesMap[note['note_id']] = note + + for note in notes: + if note['note_pid'] != "": + parent = notesMap[note['note_pid']] + + parent['children'].append(note) + parent['folder'] = True + + return rootNotes