1
0

nightly commit

This commit is contained in:
2021-01-19 22:20:11 +08:00
parent f539b7e11f
commit f64bf9a786
22 changed files with 485 additions and 293 deletions

View File

@@ -1,10 +1,40 @@
import config
import sqlite3
import json
import utils
import threading
def SafeDatabaseOperation(func):
def wrapper(self, *args, **kwargs):
with self.mutex:
# check database and acquire cursor
try:
self.check_database()
self.cursor = self.db.cursor()
except:
self.cursor = None
return (False, None)
# do real data work
try:
result = (True, func(self, *args, **kwargs))
self.cursor.close()
self.cursor = None
self.db.commit()
return result
except:
self.cursor.close()
self.cursor = None
self.db.rollback()
return (False, None)
return wrapper
class CalendarDatabase(object):
def __init__(self):
self.db = None
self.cursor = None
self.mutex = threading.Lock()
def open(self):
if (self.is_database_valid()):
@@ -21,15 +51,23 @@ class CalendarDatabase(object):
if (self.is_database_valid()):
raise Exception('Databade is opened')
# establish tables
self.open()
cursor = self.db.cursor()
with open('sql/sqlite.sql', 'r', encoding='utf-8') as fsql:
cursor = self.db.cursor()
cursor.executescript(fsql.read())
cursor.close()
self.db.commit()
#todo: finish init
# finish init
cursor.execute('INSERT INTO user VALUES (?, ?, ?, ?, ?, ?);', (
username,
utils.ComputePasswordHash(password),
1,
utils.GenerateSalt(),
utils.GenerateToken(username),
0
))
cursor.close()
self.db.commit()
def close(self):
self.check_database()
@@ -43,9 +81,80 @@ class CalendarDatabase(object):
def is_database_valid(self):
return not (self.db == None)
# operation function
def login_step1(self, username):
self.check_database()
def get_username_from_token(self, token):
self.cursor.execute('SELECT [ccn_name] FROM user WHERE [ccn_token] = ? AND [ccn_tokenExpireOn] > ?;',(
token,
utils.GetCurrentTimestamp()
))
return self.cursor.fetchone()[0]
# =============================== # =============================== operation function
# =============================== common
@SafeDatabaseOperation
def common_salt(self, username):
salt = utils.GenerateSalt()
self.cursor.execute('UPDATE user SET [ccn_salt] = ? WHERE [ccn_name] = ?;', (
salt,
username
))
return salt
@SafeDatabaseOperation
def common_login(self, username, password):
self.cursor.execute('SELECT [ccn_password], [ccn_salt] FROM user WHERE [ccn_name] = ?;', (username, ))
(gotten_salt, gotten_password) = self.cursor.fetchone()
if password == utils.ComputePasswordHashWithSalt(gotten_password, gotten_salt):
token = utils.GenerateToken(username)
self.cursor.execute('UPDATE user SET [ccn_token] = ?, [ccn_tokenExpireOn] = ? WHERE [ccn_name] = ?;', (
token,
utils.GetCurrentTimestamp() + 60 * 60 * 24 * 2, # add 2 day from now
username
))
return token
else:
# return empty string to indicate fail to login
return ''
@SafeDatabaseOperation
def common_logout(self, token):
username = self.get_username_from_token(cur, token)
self.cursor.execute('UPDATE user SET [ccn_tokenExpireOn] = 0 WHERE [ccn_name] = ?;', (username, ))
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)
return result
@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
@SafeDatabaseOperation
def common_changePassword(self, token, newpassword):
username = self.get_username_from_token(token)
self.cursor.execute('UPDATE user SET [ccn_password] = ? WHERE [ccn_name] = ?;', (
newpassword,
username
))
return None
# =============================== calendar
# =============================== collection
# =============================== todo
# =============================== admin
def login_step2(self, username, password):
self.check_database()

View File

@@ -15,7 +15,7 @@ import database
app = Flask(__name__)
render_static_resources = None
# render_static_resources = None
# =============================================database
def get_database():
@@ -39,23 +39,28 @@ def nospecHandle():
@app.route('/web/home', methods=['GET'])
def web_homeHandle():
UpdateStaticResources()
return render_template("home.html", **render_static_resources)
# UpdateStaticResources()
return render_template("home.html")
@app.route('/web/calendar', methods=['GET'])
def web_calendarHandle():
UpdateStaticResources()
return render_template("calendar.html", **render_static_resources)
# UpdateStaticResources()
return render_template("calendar.html")
@app.route('/web/todo', methods=['GET'])
def web_todoHandle():
UpdateStaticResources()
return render_template("todo.html", **render_static_resources)
# UpdateStaticResources()
return render_template("todo.html")
@app.route('/web/admin', methods=['GET'])
def web_adminHandle():
UpdateStaticResources()
return render_template("admin.html", **render_static_resources)
# UpdateStaticResources()
return render_template("admin.html")
@app.route('/web/login', methods=['GET'])
def web_loginHandle():
# UpdateStaticResources()
return render_template("login.html")
# ============================================= query page route
@@ -194,6 +199,7 @@ def api_admin_deleteHandle():
# =============================================main run
'''
def UpdateStaticResources():
global render_static_resources
if render_static_resources is not None:
@@ -209,6 +215,7 @@ def UpdateStaticResources():
'url_js_pageHome': url_for('static', filename='js/page/home.js')
}
'''
def run():
app.run(port=config.CustomConfig['web']['port'])

View File

@@ -35,7 +35,7 @@ CREATE TABLE calendar(
[ccn_title] TEXT NOT NULL,
[ccn_description] TEXT NOT NULL,
[ccn_lastChange] BIGINT NOT NULL,
[ccn_lastChange] TEXT NOT NULL,
[ccn_eventDateTimeType] TINYINT NOT NULL,
[ccn_eventDateTimeStart] BIGINT NOT NULL,
@@ -54,7 +54,7 @@ CREATE TABLE todo(
[ccn_belongTo] TEXT NOT NULL,
[ccn_data] TEXT NOT NULL,
[ccn_lastChange] BIGINT NOT NULL,
[ccn_lastChange] TEXT NOT NULL,
PRIMARY KEY (ccn_uuid),
FOREIGN KEY (ccn_belongTo) REFERENCES user(ccn_name) ON DELETE CASCADE

View File

@@ -1,4 +1,4 @@
#calendar-body div:nth-child(n+2) div {
#ccn-calendar-calendarBbody div:nth-child(n+2) div {
border-top: 0 solid black;
border-left: 0 solid black;
border-right: 1px solid black;
@@ -13,15 +13,15 @@
overflow: hidden;
}
#calendar-body div:nth-child(n+2) div:nth-child(1) {
#ccn-calendar-calendarBbody div:nth-child(n+2) div:nth-child(1) {
border-left: 1px solid black;
}
#calendar-body div:nth-child(2) div {
#ccn-calendar-calendarBbody div:nth-child(2) div {
border-top: 1px solid black;
}
#calendar-body div div {
#ccn-calendar-calendarBbody div div {
flex-grow: 1;
flex-basis: 0;
flex-shrink: 0;
@@ -29,7 +29,7 @@
overflow: hidden;
}
#calendar-body div {
#ccn-calendar-calendarBbody div {
display: flex;
flex-flow: row;
}

View File

@@ -0,0 +1,20 @@
ccn-pageName-home=coconut-leaf - A light, self-host calendar system.
ccn-pageName-calendar=coconut-leaf - Calendar
ccn-pageName-todo=coconut-leaf - Todo
ccn-pageName-admin=coconut-leaf - Admin
ccn-pageName-login=coconut-leaf - Login
ccn-header-nav-home=Home
ccn-header-nav-calendar=Calendar
ccn-header-nav-todo=Todo
ccn-header-nav-admin=Admin
ccn-header-user-login=Login
ccn-header-user-logout=Logout
ccn-header-language=Language
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>A light, self-host calendar system.</p><p>Originally, this app is served for yyc12345 personal use.</p><br /><p>Pull request / issue / translation are welcomed.</p><p>Submit them in our <a href="https://github.com/yyc12345/coconut-leaf">GitHub project</a>.</p><p>This project source code is licensed <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>.</p>
ccn-login-form-username=Username
ccn-login-form-password=Password
ccn-login-form-login=Login

View File

@@ -0,0 +1,21 @@
ccn-pageName-home=coconut-leaf - 一个轻量的自建日历系统
ccn-pageName-calendar=coconut-leaf - 日历
ccn-pageName-todo=coconut-leaf - 待办
ccn-pageName-admin=coconut-leaf - 管理
ccn-pageName-login=coconut-leaf - 登录
ccn-header-nav-home=主页
ccn-header-nav-calendar=日历
ccn-header-nav-todo=待办
ccn-header-nav-admin=管理
ccn-header-user-login=登录
ccn-header-user-logout=登出
ccn-header-language=语言
ccn-home-desc=<h1 class="title">coconut-leaf</h1><p>一个轻量的自建日历系统</p><p>原本是出于yyc12345的个人使用而制作的。</p><br /><p>欢迎提出Pull request / issue / 翻译</p><p>将他们提交到我们的<a href="https://github.com/yyc12345/coconut-leaf">GitHub项目</a>.</p><p>本工程代码使用<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL v3</a>授权。</p>
ccn-login-form-username=用户名
ccn-login-form-password=密码
ccn-login-form-login=登录

View File

@@ -2,6 +2,7 @@ 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());
@@ -9,3 +10,49 @@ function cnn_headerNav_Insert() {
});
}
function cnn_headerNav_LoggedRefresh() {
if (cnn_api_tokenValid()) {
// logged, show all nav button and logout button
$("#cnn-header-nav-home").show();
$("#cnn-header-nav-calendar").show();
$("#cnn-header-nav-todo").show();
$("#cnn-header-nav-admin").show();
$("#cnn-header-user-login").hide();
$("#cnn-header-user-logout").show();
} else {
$("#cnn-header-nav-home").show();
$("#cnn-header-nav-calendar").hide();
$("#cnn-header-nav-todo").hide();
$("#cnn-header-nav-admin").hide();
$("#cnn-header-user-login").show();
$("#cnn-header-user-logout").hide();
}
}
// bind language process and internal process function such as logout and expand menu
function cnn_headerNav_BindEvents() {
// bind function
$("#cnn-header-language > *").each(function(){
$(this).click(function(){
ccn_i18n_ChangeLanguage($(this).attr("language"));
ccn_i18n_ApplyLanguage();
});
});
// todo: bind logout
// bind burger menu
// copy from bulma website
// 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");
});
}

View File

@@ -1,5 +1,13 @@
var ccn_i18n_i18nSupported = ['en-US', 'zh-CN'];
var ccn_i18n_currentLanguage = 'en-US';
var ccn_pages_enumPages = {
home : 0,
calendar: 1,
todo: 2,
admin: 3,
login: 4
};
var ccn_pages_currentPage = ccn_pages_enumPages.home;
// judge current language
ccn_i18n_currentLanguage = ccn_localstorageAssist_Get('ccn-i18n', 'en-US');
@@ -18,14 +26,17 @@ function ccn_i18n_ChangeLanguage(newLang) {
function ccn_i18n_ApplyLanguage() {
$.i18n.properties({
name: 'strings_' + ccn_i18n_currentLanguage,
path: 'i18n/',
path: '/static/i18n/',
encoding: 'utf-8',
mode: 'map',
async: true,
cache: false,
language: ccn_i18n_currentLanguage,
callback: function() {
//set usual block
var cache = $(".ccn-i18n");
cache.each(function() {
$(this).html($.i18n.prop($(this).attr('name')));
$(this).html($.i18n.prop($(this).attr('i18n-name')));
});
//set unusual block
@@ -34,22 +45,19 @@ function ccn_i18n_ApplyLanguage() {
case ccn_pages_enumPages.home:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-home'))
break;
case ccn_pages_enumPages.user:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-user'))
case ccn_pages_enumPages.calendar:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-calendar'))
break;
case ccn_pages_enumPages.userinfo:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-userinfo'))
case ccn_pages_enumPages.todo:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-todo'))
break;
case ccn_pages_enumPages.map:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-map'))
case ccn_pages_enumPages.admin:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-admin'))
break;
case ccn_pages_enumPages.mapinfo:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-mapinfo'))
break;
case ccn_pages_enumPages.about:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-about'))
case ccn_pages_enumPages.login:
$('#ccn-pageName').html($.i18n.prop('ccn-pageName-login'))
break;
}
}
})
});
}

View File

@@ -0,0 +1,25 @@
$(document).ready(function() {
// nav process
ccn_pages_currentPage = ccn_pages_enumPages.calendar;
cnn_headerNav_Insert();
cnn_headerNav_BindEvents();
cnn_headerNav_LoggedRefresh();
// process calendar it self
ccn_calendar_LoadCalendarBody();
// apply i18n
ccn_i18n_ApplyLanguage();
});
function ccn_calendar_LoadCalendarBody() {
$.ajax({
url: $("#jsrender-tmpl-calendarItem").attr('src'),
type: "GET",
async: false,
success: function (data) {
var tmpl = $.templates(data);
$('#ccn-calendar-calendarBbody').append(tmpl.render());
}
});
}

View File

@@ -1,4 +1,10 @@
$(document).ready(function() {
// insert nav first
// nav process
ccn_pages_currentPage = ccn_pages_enumPages.home;
cnn_headerNav_Insert();
cnn_headerNav_BindEvents();
cnn_headerNav_LoggedRefresh();
// apply i18n
ccn_i18n_ApplyLanguage();
});

View File

@@ -0,0 +1,10 @@
$(document).ready(function() {
// nav process
ccn_pages_currentPage = ccn_pages_enumPages.login;
cnn_headerNav_Insert();
cnn_headerNav_BindEvents();
cnn_headerNav_LoggedRefresh();
// apply i18n
ccn_i18n_ApplyLanguage();
});

View File

@@ -0,0 +1,11 @@
{{for start=0 end=6 step=1 itemVar="~row"}}
<div>
{{for start=0 end=7 step=1 itemVar="~column"}}
<div id="ccn-calendar-calendarItem-{{:~row}}-{{:~column}}">
<p><b>1</b></p>
<p><small>春分</small></p>
<p><span class="icon is-small"><i class="fas fa-tasks"></i></span>114514</p>
</div>
{{/for}}
</div>
{{/for}}

View File

@@ -1,7 +1,7 @@
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="home">
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
<img src="/static/image/icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
@@ -14,26 +14,26 @@
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="home">Home</a>
<a class="navbar-item" href="calendar">Calendar</a>
<a class="navbar-item" href="todo">Todo</a>
<a class="navbar-item" href="admin">Admin</a>
<a i18n-name="ccn-header-nav-home" id="cnn-header-nav-home" class="navbar-item ccn-i18n" href="/web/home"></a>
<a i18n-name="ccn-header-nav-calendar" id="cnn-header-nav-calendar" class="navbar-item ccn-i18n" href="/web/calendar"></a>
<a i18n-name="ccn-header-nav-todo" id="cnn-header-nav-todo" class="navbar-item ccn-i18n" href="/web/todo"></a>
<a i18n-name="ccn-header-nav-admin" id="cnn-header-nav-admin" class="navbar-item ccn-i18n" href="/web/admin"></a>
</div>
<div class="navbar-end">
<p class="control">
<a class="button is-primary">Login</a>
<p id="cnn-header-user-login" class="navbar-item">
<a class="button is-primary ccn-i18n" i18n-name="ccn-header-user-login" href="/web/login"></a>
</p>
<p class="control">
<a class="button is-primary">Logout</a>
<p id="cnn-header-user-logout" class="navbar-item">
<a class="button is-primary ccn-i18n" i18n-name="ccn-header-user-logout"></a>
</p>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Language</a>
<a class="navbar-link ccn-i18n" i18n-name="ccn-header-language"></a>
<div class="navbar-dropdown">
<a class="navbar-item">English</a>
<a class="navbar-item">简体中文</a>
<div id="cnn-header-language" class="navbar-dropdown">
<a language="en-US" class="navbar-item">English</a>
<a language="zh-CN" class="navbar-item">简体中文</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,37 @@
<div class="schedule-day container">
<div class="schedule-day-words">
<b>13</b>
<b>Friday</b>
</div>
<div class="schedule-event-list">
<div class="schedule-event card">
<div class="schedule-event-words">
<p class="level-item"><b>This is
titleewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
</p>
<p class="level-item">this is
subtitleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
</p>
</div>
<div class="schedule-event-icon">
<span class="icon is-small"><i class="fas fa-lock"></i></span>
</div>
</div>
<div class="schedule-event card">
<div class="schedule-event-words">
<p class="level-item"><b>This is
titleewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww</b>
</p>
<p class="level-item">this is
subtitleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
</p>
</div>
<div class="schedule-event-icon">
<span class="icon is-small"><i class="fas fa-lock"></i></span>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,27 @@
<div class="todo-item card">
<div class="todo-item-words">
<p>this is a
namewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
</p>
</div>
<div class="todo-item-icon control">
<button class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></button>
</div>
<div class="todo-item-icon control">
<button class="button"><span class="icon is-small"><i class="fas fa-trash"></i></button>
</div>
</div>
<div class="todo-item card">
<div class="todo-item-words control">
<textarea class="textarea"></textarea>
</div>
<div class="todo-item-icon control">
<button class="button"><span class="icon is-small"><i class="fas fa-check"></i></span></button>
</div>
<div class="todo-item-icon control">
<button class="button"><span class="icon is-small"><i class="fas fa-times"></i></button>
</div>
</div>

View File

@@ -0,0 +1,27 @@
<div class="user-item card">
<div class="user-item-words">
<p><b>this is a name</b></p>
</div>
<div class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-pen"></i></span></a>
</div>
<div class="user-item-icon control">
<a class="button"><span class="icon is-small"><i class="fas fa-trash"></i></span></a>
</div>
</div>
<div class="user-item card">
<div class="user-item-words">
<p><b>this is a name</b></p>
<div class="control">
<input class="input" type="password" placeholder="New password"></input>
</div>
</div>
<div class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-check"></i></span></a>
</div>
<div class="user-item-icon control">
<a class="button" href="javascript:void(0);"><span class="icon is-small"><i class="fas fa-times"></i></span></a>
</div>
</div>

View File

@@ -3,54 +3,27 @@
<head>
<meta charset="utf-8">
<title>coconut-leaf Admin</title>
<title id="ccn-pageName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
<link rel="stylesheet" href="admin.css">
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="/static/tmpl/headerNav.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-userItem" src="/static/tmpl/userItem.tmpl"></script>
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
<script type="text/javascript" src="/static/js/i18n.js"></script>
<script type="text/javascript" src="/static/js/api.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="/static/js/page/login.js"></script>
<link rel="stylesheet" href="/static/css/admin.css">
</head>
<body>
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="home">
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="home">Calendar</a>
<a class="navbar-item" href="home">Todo</a>
<a class="navbar-item" href="about">About</a>
</div>
<div class="navbar-end">
<p class="control">
<a class="button is-primary" href="javascript:void(0);">Logout</a>
</p>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Language</a>
<div class="navbar-dropdown">
<a href="javascript:void(0);" class="navbar-item">English</a>
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
</div>
</div>
</div>
</div>
</nav>
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<h1 class="title">My account</h1>
<div class="field">

View File

@@ -3,53 +3,28 @@
<head>
<meta charset="utf-8">
<title>coconut-leaf Calendar</title>
<title id="ccn-pageName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
<link rel="stylesheet" href="calendar.css">
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="/static/tmpl/headerNav.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-calendarItem" src="/static/tmpl/calendarItem.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-scheduleItem" src="/static/tmpl/scheduleItem.tmpl"></script>
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
<script type="text/javascript" src="/static/js/i18n.js"></script>
<script type="text/javascript" src="/static/js/api.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="/static/js/page/calendar.js"></script>
<link rel="stylesheet" href="/static/css/calendar.css">
</head>
<body>
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="home">
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="home">Calendar</a>
<a class="navbar-item" href="home">Todo</a>
<a class="navbar-item" href="about">About</a>
</div>
<div class="navbar-end">
<p class="control">
<a class="button is-primary" href="javascript:void(0);">Logout</a>
</p>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Language</a>
<div class="navbar-dropdown">
<a href="javascript:void(0);" class="navbar-item">English</a>
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
</div>
</div>
</div>
</div>
</nav>
<div class="container" style="margin-top: 20px;">
<div class="tabs">
@@ -82,7 +57,7 @@
</div>
</nav>
<div id="calendar-body" class="card" style="padding: 1.25rem; display: flex; flex-flow: column;">
<div id="ccn-calendar-calendarBbody" class="card" style="padding: 1.25rem; display: flex; flex-flow: column;">
<div style="margin: 0 0 0.75em 0;">
<div><b>Monday</b></div>
<div><b>Tuesday</b></div>
@@ -92,65 +67,6 @@
<div><b style="color: red;">Saturday</b></div>
<div><b style="color: red;">Sunday</b></div>
</div>
<div>
<div>
<p><b>1</b></p>
<p><small>春分</small></p>
<p><span class="icon is-small"><i class="fas fa-tasks"></i></span>114514</p>
</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
<div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
<div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
<div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
<div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
<div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</div>
</div>
<div class="container" style="padding: 1.25rem; display: flex; flex-flow: column; margin-top: 1.25rem;">

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<title>coconut-leaf Login</title>
<title id="ccn-pageName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
@@ -11,26 +11,19 @@
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="{{url_tmpl_headerNac}}"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="/static/tmpl/headerNav.tmpl"></script>
<script type="text/javascript" src="{{url_js_localStorageAssist}}"></script>
<script type="text/javascript" src="{{url_js_i18n}}"></script>
<script type="text/javascript" src="{{url_js_api}}"></script>
<script type="text/javascript" src="{{url_js_headerNav}}"></script>
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
<script type="text/javascript" src="/static/js/i18n.js"></script>
<script type="text/javascript" src="/static/js/api.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="{{url_js_pageHome}}"></script>
<script type="text/javascript" src="/static/js/page/home.js"></script>
</head>
<body>
<div class="container" style="margin-top: 1.25rem;">
<article>
<h1 class="title">coconut-leaf</h1>
<p>A light, self-host calendar system.</p>
<p>Originally, this app is served for yyc12345's personal use.</p>
<br />
<p>Pull request / issue / translation are welcomed.</p>
<p>Submit them in our <a href="https://github.com/yyc12345/coconut-leaf">GitHub project</a>.</p>
<article class="ccn-i18n" i18n-name="ccn-home-desc">
</article>
</div>
</body>

View File

@@ -3,53 +3,30 @@
<head>
<meta charset="utf-8">
<title>coconut-leaf Login</title>
<title id="ccn-pageName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="/static/tmpl/headerNav.tmpl"></script>
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
<script type="text/javascript" src="/static/js/i18n.js"></script>
<script type="text/javascript" src="/static/js/api.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="/static/js/page/login.js"></script>
</head>
<body style="height: 100%; display: flex; flex-flow: column;">
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="home">
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="home">Home</a>
<a class="navbar-item" href="about">About</a>
</div>
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Language</a>
<div class="navbar-dropdown">
<a class="navbar-item">English</a>
<a class="navbar-item">简体中文</a>
</div>
</div>
</div>
</div>
</nav>
<div style="height: 100%; width: 100%; display: flex; justify-content: center; align-items: center;">
<div class="card" style="padding: 1.25rem;">
<div class="field">
<label class="label">Username</label>
<label class="label ccn-i18n" i18n-name="ccn-login-form-username"></label>
<div class="control has-icons-left has-icons-right">
<input class="input" type="text">
<span class="icon is-small is-left">
@@ -58,7 +35,7 @@
</div>
</div>
<div class="field">
<label class="label">Password</label>
<label class="label ccn-i18n" i18n-name="ccn-login-form-password"></label>
<p class="control has-icons-left">
<input class="input" type="password">
<span class="icon is-small is-left">
@@ -68,7 +45,7 @@
</div>
<div class="control">
<button class="button is-primary">Login</button>
<button class="button is-primary ccn-i18n" i18n-name="ccn-login-form-login"></button>
</div>
</div>

View File

@@ -3,53 +3,27 @@
<head>
<meta charset="utf-8">
<title>coconut-leaf Calendar</title>
<title id="ccn-pageName"></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.12.1/js/all.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery-i18n-properties@1.2.7/jquery.i18n.properties.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jsrender@1.0.10/jsrender.min.js"></script>
<link rel="stylesheet" href="todo.css">
<script type="text/x-jsrender" id="jsrender-tmpl-headerNav" src="/static/tmpl/headerNav.tmpl"></script>
<script type="text/x-jsrender" id="jsrender-tmpl-todoItem" src="/static/tmpl/todoItem.tmpl"></script>
<script type="text/javascript" src="/static/js/localStorageAssist.js"></script>
<script type="text/javascript" src="/static/js/i18n.js"></script>
<script type="text/javascript" src="/static/js/api.js"></script>
<script type="text/javascript" src="/static/js/headerNav.js"></script>
<script type="text/javascript" src="/static/js/page/login.js"></script>
<link rel="stylesheet" href="/static/css/todo.css">
</head>
<body>
<nav class="navbar has-shadow is-spaced bd-navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="home">
<img src="icon.png"><b style="margin:0 0 0 14px;">coconut-leaf</b>
</a>
<a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false"
data-target="navbarBasicExample">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="home">Calendar</a>
<a class="navbar-item" href="home">Todo</a>
<a class="navbar-item" href="about">About</a>
</div>
<div class="navbar-end">
<p class="control">
<a class="button is-primary" href="javascript:void(0);">Logout</a>
</p>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">Language</a>
<div class="navbar-dropdown">
<a href="javascript:void(0);" class="navbar-item">English</a>
<a href="javascript:void(0);" class="navbar-item">简体中文</a>
</div>
</div>
</div>
</div>
</nav>
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<h1 class="title">Todo list</h1>

View File

@@ -1,6 +1,7 @@
import hashlib
import random
import uuid
import time
ValidUsername = set(map(lambda x:chr(x), range(48, 58, 1))) | set(map(lambda x:chr(x), range(65, 91, 1))) | set(map(lambda x:chr(x), range(97, 123, 1)))
ValidPassword = set(map(lambda x:chr(x), range(33, 127, 1)))
@@ -13,7 +14,7 @@ def IsValidPassword(strl):
def ComputePasswordHash(password):
s = hashlib.sha256()
s.update(password)
s.update(password.encode('utf-8'))
return s.hexdigest()
def GenerateUUID():
@@ -21,8 +22,8 @@ def GenerateUUID():
def GenerateToken(username):
s = hashlib.sha256()
s.update(username)
s.update(str(GenerateSalt()))
s.update(username.encode('utf-8'))
s.update((str(GenerateSalt())).encode('utf-8'))
return s.hexdigest()
def GenerateSalt():
@@ -30,5 +31,8 @@ def GenerateSalt():
def ComputePasswordHashWithSalt(passwordHashed, salt):
s = hashlib.sha256()
s.update(passwordHashed + str(salt))
s.update((passwordHashed + str(salt)).encode('utf-8'))
return s.hexdigest()
def GetCurrentTimestamp():
return int(time.time())