From b83b19364c34699b78bf413d970af10c803d7db1 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 24 Jan 2021 14:38:08 +0800 Subject: [PATCH] nightly commit --- src/database.py | 100 +++++++++++++++-------- src/server.py | 4 +- src/sql/sqlite.sql | 8 +- src/static/i18n/strings_en-US.properties | 12 ++- src/static/i18n/strings_zh-CN.properties | 12 ++- src/static/js/headerNav.js | 18 ++-- src/static/js/i18n.js | 10 +-- src/static/js/page/admin.js | 14 ++++ src/static/js/page/calendar.js | 6 +- src/static/js/page/home.js | 6 +- src/static/js/page/login.js | 6 +- src/static/js/page/todo.js | 39 +++------ src/static/js/template.js | 61 ++++++++++++++ src/static/js/utils.js | 4 + src/static/tmpl/todoItem.tmpl | 2 +- src/templates/admin.html | 1 + src/templates/calendar.html | 17 ++-- src/templates/home.html | 1 + src/templates/login.html | 1 + src/templates/todo.html | 1 + src/utils.py | 7 +- 21 files changed, 223 insertions(+), 107 deletions(-) create mode 100644 src/static/js/template.js diff --git a/src/database.py b/src/database.py index a0a585d..b37df6e 100644 --- a/src/database.py +++ b/src/database.py @@ -3,6 +3,7 @@ import sqlite3 import json import utils import threading +import logging def SafeDatabaseOperation(func): def wrapper(self, *args, **kwargs): @@ -11,22 +12,31 @@ def SafeDatabaseOperation(func): try: self.check_database() self.cursor = self.db.cursor() - except: + except Exception as e: self.cursor = None - return (False, None) + if config.CustomConfig['debug']: + logging.exception(e) + return (False, str(e), None) # do real data work try: - result = (True, func(self, *args, **kwargs)) + if self.isFirstRun: + self.isFirstRun = False + print('Cleaning outdated token...') + self.tokenOper_clean() + + result = (True, '', func(self, *args, **kwargs)) self.cursor.close() self.cursor = None self.db.commit() return result - except: + except Exception as e: self.cursor.close() self.cursor = None self.db.rollback() - return (False, None) + if config.CustomConfig['debug']: + logging.exception(e) + return (False, str(e), None) return wrapper @@ -35,6 +45,7 @@ class CalendarDatabase(object): self.db = None self.cursor = None self.mutex = threading.Lock() + self.isFirstRun = True def open(self): if (self.is_database_valid()): @@ -49,7 +60,7 @@ class CalendarDatabase(object): def init(self, username, password): if (self.is_database_valid()): - raise Exception('Databade is opened') + raise Exception('Database is opened') # establish tables self.open() @@ -58,13 +69,11 @@ class CalendarDatabase(object): cursor.executescript(fsql.read()) # finish init - cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?, ?, ?);', ( + cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?);', ( username, utils.ComputePasswordHash(password), 1, - utils.GenerateSalt(), - utils.GenerateToken(username), - 0 + utils.GenerateSalt() )) cursor.close() self.db.commit() @@ -81,13 +90,32 @@ class CalendarDatabase(object): def is_database_valid(self): return not (self.db == None) - def get_username_from_token(self, token): - self.cursor.execute('SELECT [ccn_name] FROM user WHERE [ccn_token] = ? AND [ccn_tokenExpireOn] > ?;',( + # ======================= token related internal operation + def tokenOper_clean(self): + # remove outdated token + self.cursor.execute('DELETE FROM token WHERE [ccn_tokenExpireOn] <= ?',(utils.GetCurrentTimestamp(), )) + + def tokenOper_postpone_expireOn(self, token): + self.cursor.execute('UPDATE token SET [ccn_tokenExpireOn] = ? WHERE [ccn_token] = ?;', ( + utils.GetTokenExpireOn(), + token + )) + + def tokenOper_check_valid(self, token): + self.tokenOper_get_username(token) + + def tokenOper_is_admin(self, username): + self.cursor.execute('SELECT [ccn_isAdmin] FROM user WHERE [ccn_name] = ?;',(username, )) + cache = self.cursor.fetchone()[0] + return True if cache == 1 else False + + def tokenOper_get_username(self, token): + self.cursor.execute('SELECT [ccn_user] FROM token WHERE [ccn_token] = ? AND [ccn_tokenExpireOn] > ?;',( token, utils.GetCurrentTimestamp() )) return self.cursor.fetchone()[0] - + # =============================== # =============================== operation function # =============================== common @SafeDatabaseOperation @@ -106,16 +134,19 @@ class CalendarDatabase(object): if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt): token = utils.GenerateToken(username) - self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ?, [ccn_salt] = ? WHERE [ccn_name] = ?;', ( - token, - utils.GetCurrentTimestamp() + 60 * 60 * 24 * 2, # add 2 day from now + self.cursor.execute('UPDATE user SET [ccn_salt] = ? WHERE [ccn_name] = ?;', ( utils.GenerateSalt(), # regenerate a new slat to prevent re-login try username )) + self.cursor.execute('INSERT INTO token VALUES (?, ?, ?);', ( + username, + token, + utils.GetTokenExpireOn(), # add 2 day from now + )) return token else: # throw a exception to indicate fail to login - raise Exception() + raise Exception('Login authentication failed') @SafeDatabaseOperation def common_webLogin(self, username, password): @@ -123,38 +154,35 @@ class CalendarDatabase(object): if len(self.cursor.fetchall()) != 0: token = utils.GenerateToken(username) - self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ? WHERE [ccn_name] = ?;', ( + self.cursor.execute('INSERT INTO token VALUES (?, ?, ?);', ( + username, token, - utils.GetCurrentTimestamp() + 60 * 60 * 24 * 2, # add 2 day from now - username + utils.GetTokenExpireOn(), # add 2 day from now )) return token else: # throw a exception to indicate fail to login - raise Exception() + raise Exception('Login authentication failed') @SafeDatabaseOperation def common_logout(self, token): - username = self.get_username_from_token(token) - self.cursor.execute('UPDATE user SET [ccn_tokenExpireOn] = 0 WHERE [ccn_name] = ?;', (username, )) + self.tokenOper_check_valid(token) + self.cursor.execute('DELETE FROM token WHERE [ccn_token] = ?;', (token, )) return None @SafeDatabaseOperation def common_tokenValid(self, token): - # get user name have check the validation, don't do anything more. - self.get_username_from_token(token) + self.tokenOper_check_valid(token) return None @SafeDatabaseOperation def common_isAdmin(self, token): - username = self.get_username_from_token(token) - self.cursor.execute('SELECT [ccn_isAdmin] FROM user WHERE [ccn_name] = ?;', (username, )) - result = self.cursor.fetchone()[0] == 1 - return result + username = self.tokenOper_get_username(token) + return self.tokenOper_is_admin(username) @SafeDatabaseOperation def common_changePassword(self, token, newpassword): - username = self.get_username_from_token(token) + username = self.tokenOper_get_username(token) self.cursor.execute('UPDATE user SET [ccn_password] = ? WHERE [ccn_name] = ?;', ( newpassword, username @@ -170,25 +198,25 @@ class CalendarDatabase(object): # =============================== todo @SafeDatabaseOperation def todo_getFull(self, token): - username = self.get_username_from_token(token) + username = self.tokenOper_get_username(token) self.cursor.execute('SELECT * FROM todo WHERE [ccn_belongTo] = ?;', (username, )) return self.cursor.fetchall() @SafeDatabaseOperation def todo_getList(self, token): - username = self.get_username_from_token(token) + username = self.tokenOper_get_username(token) self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_belongTo] = ?;', (username, )) return tuple(map(lambda x: x[0], self.cursor.fetchall())) @SafeDatabaseOperation def todo_getDetail(self, token, uuid): - username = self.get_username_from_token(token) + username = self.tokenOper_get_username(token) self.cursor.execute('SELECT * FROM todo WHERE [ccn_belongTo] = ? AND [ccn_uuid] = ?;', (username, uuid)) return self.cursor.fetchone() @SafeDatabaseOperation def todo_add(self, token): - username = self.get_username_from_token(token) + username = self.tokenOper_get_username(token) newuuid = utils.GenerateUUID() lastupdate = utils.GenerateUUID() returnedData = ( @@ -203,7 +231,7 @@ class CalendarDatabase(object): @SafeDatabaseOperation def todo_update(self, token, uuid, data, lastChange): # check valid token - self.get_username_from_token(token) + self.tokenOper_check_valid(token) # check sync conflict self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_uuid] = ? AND [ccn_lastChange] = ?;', ( uuid, @@ -224,7 +252,7 @@ class CalendarDatabase(object): @SafeDatabaseOperation def todo_delete(self, token, uuid, lastChange): # check valid token - self.get_username_from_token(token) + self.tokenOper_check_valid(token) # check sync conflict self.cursor.execute('SELECT [ccn_uuid] FROM todo WHERE [ccn_uuid] = ? AND [ccn_lastChange] = ?;', ( uuid, diff --git a/src/server.py b/src/server.py index c72bd06..12e964e 100644 --- a/src/server.py +++ b/src/server.py @@ -302,8 +302,8 @@ def CheckParameter(paramList): def ConstructResponseBody(returnedTuple): return { 'success': returnedTuple[0], - 'error': '', - 'data': returnedTuple[1] + 'error': returnedTuple[1], + 'data': returnedTuple[2] } def run(): diff --git a/src/sql/sqlite.sql b/src/sql/sqlite.sql index 9f2a2da..7168759 100644 --- a/src/sql/sqlite.sql +++ b/src/sql/sqlite.sql @@ -6,10 +6,16 @@ CREATE TABLE user( [ccn_password] TEXT NOT NULL, [ccn_isAdmin] TINYINT NOT NULL CHECK(ccn_isAdmin = 1 OR ccn_isAdmin = 0), [ccn_salt] INTEGER NOT NULL, + + PRIMARY KEY (ccn_name) +); + +CREATE TABLE token( + [ccn_user] TEXT NOT NULL, [ccn_token] TEXT UNIQUE NOT NULL, [ccn_tokenExpireOn] BIGINT NOT NULL, - PRIMARY KEY (ccn_name) + FOREIGN KEY (ccn_user) REFERENCES user(ccn_name) ON DELETE CASCADE ); CREATE TABLE collection( diff --git a/src/static/i18n/strings_en-US.properties b/src/static/i18n/strings_en-US.properties index e084b92..154fe60 100644 --- a/src/static/i18n/strings_en-US.properties +++ b/src/static/i18n/strings_en-US.properties @@ -24,10 +24,10 @@ ccn-login-form-login=Login ccn-todo-todoList=Todo list -ccn-calendar-jump=Jump -ccn-calendar-today=Today -ccn-calendar-add=Add... -ccn-calendar-scheduleList=Schedule +ccn-calendar-calendar-jump=Jump +ccn-calendar-calendar-today=Today +ccn-calendar-calendar-add=Add... +ccn-calendar-calendar-scheduleList=Schedule ccn-calendar-tabcontrol-tabCalendar=Calendar ccn-calendar-tabcontrol-tabShared=Shared ccn-calendar-tabcontrol-tabSharing=Sharing @@ -38,3 +38,7 @@ ccn-calendar-week-thursday=Thursday ccn-calendar-week-friday=Friday ccn-calendar-week-saturday=Saturday ccn-calendar-week-sunday=Sunday +ccn-calendar-shared-list=Shared +ccn-calendar-sharing-ownedList=Owned +ccn-calendar-sharing-sharingTargetList=Sharing target +ccn-calendar-sharing-sharingTargetEditing=Editing: diff --git a/src/static/i18n/strings_zh-CN.properties b/src/static/i18n/strings_zh-CN.properties index 25b2217..6129f6e 100644 --- a/src/static/i18n/strings_zh-CN.properties +++ b/src/static/i18n/strings_zh-CN.properties @@ -24,10 +24,10 @@ ccn-login-form-login=登录 ccn-todo-todoList=待办列表 -ccn-calendar-jump=转到 -ccn-calendar-today=今天 -ccn-calendar-add=添加... -ccn-calendar-scheduleList=日程安排 +ccn-calendar-calendar-jump=转到 +ccn-calendar-calendar-today=今天 +ccn-calendar-calendar-add=添加... +ccn-calendar-calendar-scheduleList=日程安排 ccn-calendar-tabcontrol-tabCalendar=日历 ccn-calendar-tabcontrol-tabShared=被共享的 ccn-calendar-tabcontrol-tabSharing=共享给其他人 @@ -38,3 +38,7 @@ ccn-calendar-week-thursday=星期四 ccn-calendar-week-friday=星期五 ccn-calendar-week-saturday=星期六 ccn-calendar-week-sunday=星期日 +ccn-calendar-shared-list=被共享的集合 +ccn-calendar-sharing-ownedList=我的集合 +ccn-calendar-sharing-sharingTargetList=分享目标 +ccn-calendar-sharing-sharingTargetEditing=正在编辑集合: diff --git a/src/static/js/headerNav.js b/src/static/js/headerNav.js index d71d576..36c604f 100644 --- a/src/static/js/headerNav.js +++ b/src/static/js/headerNav.js @@ -1,13 +1,5 @@ function cnn_headerNav_Insert() { - $.ajax({ - url: $("#jsrender-tmpl-headerNav").attr('src'), - type: "GET", - async: false, - success: function (data) { - var tmpl = $.templates(data); - $('body').prepend(tmpl.render()); - } - }); + $('body').prepend(ccn_template_headerNav.render()); } function cnn_headerNav_LoggedRefresh() { @@ -57,10 +49,10 @@ function cnn_headerNav_BindEvents() { // Check for click events on the navbar burger icon $(".navbar-burger").click(function() { - // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu" - $(".navbar-burger").toggleClass("is-active"); - $(".navbar-menu").toggleClass("is-active"); + // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu" + $(".navbar-burger").toggleClass("is-active"); + $(".navbar-menu").toggleClass("is-active"); -}); + }); } diff --git a/src/static/js/i18n.js b/src/static/js/i18n.js index 85b8831..aae0ed0 100644 --- a/src/static/js/i18n.js +++ b/src/static/js/i18n.js @@ -43,19 +43,19 @@ function ccn_i18n_ApplyLanguage() { //set title switch(ccn_pages_currentPage) { case ccn_pages_enumPages.home: - $('#ccn-pageName').html($.i18n.prop('ccn-pageName-home')) + $('#ccn-pageName').html($.i18n.prop('ccn-pageName-home')); break; case ccn_pages_enumPages.calendar: - $('#ccn-pageName').html($.i18n.prop('ccn-pageName-calendar')) + $('#ccn-pageName').html($.i18n.prop('ccn-pageName-calendar')); break; case ccn_pages_enumPages.todo: - $('#ccn-pageName').html($.i18n.prop('ccn-pageName-todo')) + $('#ccn-pageName').html($.i18n.prop('ccn-pageName-todo')); break; case ccn_pages_enumPages.admin: - $('#ccn-pageName').html($.i18n.prop('ccn-pageName-admin')) + $('#ccn-pageName').html($.i18n.prop('ccn-pageName-admin')); break; case ccn_pages_enumPages.login: - $('#ccn-pageName').html($.i18n.prop('ccn-pageName-login')) + $('#ccn-pageName').html($.i18n.prop('ccn-pageName-login')); break; } } diff --git a/src/static/js/page/admin.js b/src/static/js/page/admin.js index e69de29..368e489 100644 --- a/src/static/js/page/admin.js +++ b/src/static/js/page/admin.js @@ -0,0 +1,14 @@ +$(document).ready(function() { + ccn_pages_currentPage = ccn_pages_enumPages.admin; + + // template process + ccn_template_Load(); + + // nav process + cnn_headerNav_Insert(); + cnn_headerNav_BindEvents(); + cnn_headerNav_LoggedRefresh(); + + // apply i18n + ccn_i18n_ApplyLanguage(); +}); \ No newline at end of file diff --git a/src/static/js/page/calendar.js b/src/static/js/page/calendar.js index 609a86d..f518af0 100644 --- a/src/static/js/page/calendar.js +++ b/src/static/js/page/calendar.js @@ -1,6 +1,10 @@ $(document).ready(function() { - // nav process ccn_pages_currentPage = ccn_pages_enumPages.calendar; + + // template process + ccn_template_Load(); + + // nav process cnn_headerNav_Insert(); cnn_headerNav_BindEvents(); cnn_headerNav_LoggedRefresh(); diff --git a/src/static/js/page/home.js b/src/static/js/page/home.js index 5c79ac9..10ef276 100644 --- a/src/static/js/page/home.js +++ b/src/static/js/page/home.js @@ -1,6 +1,10 @@ $(document).ready(function() { - // nav process ccn_pages_currentPage = ccn_pages_enumPages.home; + + // template process + ccn_template_Load(); + + // nav process cnn_headerNav_Insert(); cnn_headerNav_BindEvents(); cnn_headerNav_LoggedRefresh(); diff --git a/src/static/js/page/login.js b/src/static/js/page/login.js index 99e2921..91cdbb6 100644 --- a/src/static/js/page/login.js +++ b/src/static/js/page/login.js @@ -1,6 +1,10 @@ $(document).ready(function() { - // nav process ccn_pages_currentPage = ccn_pages_enumPages.login; + + // template process + ccn_template_Load(); + + // nav process cnn_headerNav_Insert(); cnn_headerNav_BindEvents(); cnn_headerNav_LoggedRefresh(); diff --git a/src/static/js/page/todo.js b/src/static/js/page/todo.js index 1bd64c5..3257fec 100644 --- a/src/static/js/page/todo.js +++ b/src/static/js/page/todo.js @@ -1,8 +1,12 @@ var ccn_todo_todoListCache = []; $(document).ready(function() { + ccn_pages_currentPage = ccn_pages_enumPages.todo; + + // template process + ccn_template_Load(); + // nav process - ccn_pages_currentPage = ccn_pages_enumPages.login; cnn_headerNav_Insert(); cnn_headerNav_BindEvents(); cnn_headerNav_LoggedRefresh(); @@ -40,25 +44,15 @@ function ccn_todo_RenderCacheList() { data: undefined }; - var templates = undefined; - $.ajax({ - url: $("#jsrender-tmpl-todoItem").attr('src'), - type: "GET", - async: false, - success: function (data) { - templates = $.templates(data); - } - }); - var listDOM = $("#ccn-todo-todoList"); for(var index in ccn_todo_todoListCache) { // update render data var item = ccn_todo_todoListCache[index]; renderdata.uuid = item[0]; - renderdata.data = item[2]; + renderdata.data = LineBreaker2Br(item[2]); // render - listDOM.append(templates.render(renderdata)); + listDOM.append(ccn_template_todoItem.render(renderdata)); // set mode var uuid = renderdata.uuid; @@ -110,22 +104,11 @@ function ccn_todo_Add() { // add into cache ccn_todo_todoListCache[result[0]] = result; - // render - var templates = undefined; - $.ajax({ - url: $("#jsrender-tmpl-todoItem").attr('src'), - type: "GET", - async: false, - success: function (data) { - templates = $.templates(data); - } - }); - // render var listDOM = $("#ccn-todo-todoList"); - listDOM.append(templates.render({ + listDOM.append(ccn_template_todoItem.render({ uuid: result[0], - data: result[2] + data: LineBreaker2Br(result[2]) })); // set mode @@ -145,7 +128,7 @@ function ccn_todo_ItemEdit() { // copy current data to textarea $("#ccn-todo-todoItem-textarea-" + uuid).val( - $("#ccn-todo-todoItem-p-" + uuid).text() + ccn_todo_todoListCache[uuid][2] ); // switch to edit mode @@ -186,7 +169,7 @@ function ccn_todo_ItemUpdate() { // safely update data & lastChanged and control ccn_todo_todoListCache[uuid][2] = newData; ccn_todo_todoListCache[uuid][3] = result; - $("#ccn-todo-todoItem-p-" + uuid).text(newData); + $("#ccn-todo-todoItem-p-" + uuid).html(LineBreaker2Br(newData)); // switch to normal mode ccn_todo_ChangeDisplayMode(uuid, false); diff --git a/src/static/js/template.js b/src/static/js/template.js new file mode 100644 index 0000000..2f1029f --- /dev/null +++ b/src/static/js/template.js @@ -0,0 +1,61 @@ +var ccn_template_headerNav = undefined; +var ccn_template_calendarItem = undefined; +var ccn_template_scheduleItem = undefined; +var ccn_template_userItem = undefined; +var ccn_template_todoItem = undefined; + +function ccn_template_Load() { + $.ajax({ + url: $("#jsrender-tmpl-headerNav").attr('src'), + type: "GET", + async: false, + success: function (data) { + ccn_template_headerNav = $.templates(data); + } + }); + + switch(ccn_pages_currentPage) { + case ccn_pages_enumPages.home: + break; + case ccn_pages_enumPages.calendar: + $.ajax({ + url: $("#jsrender-tmpl-calendarItem").attr('src'), + type: "GET", + async: false, + success: function (data) { + ccn_template_calendarItem = $.templates(data); + } + }); + $.ajax({ + url: $("#jsrender-tmpl-scheduleItem").attr('src'), + type: "GET", + async: false, + success: function (data) { + ccn_template_scheduleItem = $.templates(data); + } + }); + break; + case ccn_pages_enumPages.todo: + $.ajax({ + url: $("#jsrender-tmpl-todoItem").attr('src'), + type: "GET", + async: false, + success: function (data) { + ccn_template_todoItem = $.templates(data); + } + }); + break; + case ccn_pages_enumPages.admin: + $.ajax({ + url: $("#jsrender-tmpl-userItem").attr('src'), + type: "GET", + async: false, + success: function (data) { + ccn_template_userItem = $.templates(data); + } + }); + break; + case ccn_pages_enumPages.login: + break; + } +} \ No newline at end of file diff --git a/src/static/js/utils.js b/src/static/js/utils.js index 1d7f233..933b621 100644 --- a/src/static/js/utils.js +++ b/src/static/js/utils.js @@ -36,3 +36,7 @@ function GetApiToken() { function SetApiToken(value) { ccn_localstorageAssist_Set('ccn-token', value); } + +function LineBreaker2Br(strl) { + return $('
').text(strl).html().replace(/\n/g,'
'); +} \ No newline at end of file diff --git a/src/static/tmpl/todoItem.tmpl b/src/static/tmpl/todoItem.tmpl index e88b7d7..ca8add9 100644 --- a/src/static/tmpl/todoItem.tmpl +++ b/src/static/tmpl/todoItem.tmpl @@ -1,6 +1,6 @@
-

{{>data}}

+

{{:data}}

diff --git a/src/templates/admin.html b/src/templates/admin.html index 73e1ba0..b2a7388 100644 --- a/src/templates/admin.html +++ b/src/templates/admin.html @@ -18,6 +18,7 @@ + diff --git a/src/templates/calendar.html b/src/templates/calendar.html index be232a9..3925c36 100644 --- a/src/templates/calendar.html +++ b/src/templates/calendar.html @@ -19,6 +19,7 @@ + @@ -46,16 +47,16 @@
- +
- +
- +
@@ -72,7 +73,7 @@
-

+