Browse Source

Merge pull request #47 from helloxz/dev

0.9.16
pull/90/head
xiaoz 3 years ago committed by GitHub
parent
commit
70f22c1e79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 216
      class/Api.php
  3. 52
      controller/api.php
  4. 3
      controller/login.php
  5. 10
      data/update.log
  6. 1
      db/index.html
  7. BIN
      db/onenav.simple.db3
  8. 7
      db/sql/20220308.sql
  9. 11
      db/sql/on_db_logs.sql
  10. 9
      templates/admin/add_category.php
  11. 10
      templates/admin/edit_category.php
  12. 9
      templates/admin/index.php
  13. 37
      templates/admin/static/embed.js
  14. 7
      templates/default/index.php
  15. 2
      version.txt

2
README.md

@ -42,7 +42,7 @@ docker run -itd --name="onenav" -p 80:80 \
* `PASSWORD`:设置密码,上述设置为`xiaoz.me` * `PASSWORD`:设置密码,上述设置为`xiaoz.me`
* `/data/onenav`:本机挂载目录,用于持久存储Onenav数据 * `/data/onenav`:本机挂载目录,用于持久存储Onenav数据
> 更多说明,请参考帮助文档:https://www.yuque.com/helloz/onenav > 更多说明,请参考帮助文档:https://dwz.ovh/onenav
## Demo ## Demo

216
class/Api.php

@ -15,14 +15,15 @@ class Api {
/** /**
* name:创建分类目录 * name:创建分类目录
*/ */
public function add_category($token,$name,$property = 0,$weight = 0,$description = ''){ public function add_category($token,$name,$property = 0,$weight = 0,$description = '',$font_icon = ''){
$this->auth($token); $this->auth($token);
$data = [ $data = [
'name' => htmlspecialchars($name,ENT_QUOTES), 'name' => htmlspecialchars($name,ENT_QUOTES),
'add_time' => time(), 'add_time' => time(),
'weight' => $weight, 'weight' => $weight,
'property' => $property, 'property' => $property,
'description' => htmlspecialchars($description,ENT_QUOTES) 'description' => htmlspecialchars($description,ENT_QUOTES),
'font_icon' => $font_icon
]; ];
//插入分类目录 //插入分类目录
$this->db->insert("on_categorys",$data); $this->db->insert("on_categorys",$data);
@ -46,7 +47,7 @@ class Api {
* 修改分类目录 * 修改分类目录
* *
*/ */
public function edit_category($token,$id,$name,$property = 0,$weight = 0,$description = ''){ public function edit_category($token,$id,$name,$property = 0,$weight = 0,$description = '',$font_icon = ''){
$this->auth($token); $this->auth($token);
//如果id为空 //如果id为空
if( empty($id) ){ if( empty($id) ){
@ -63,7 +64,8 @@ class Api {
'up_time' => time(), 'up_time' => time(),
'weight' => $weight, 'weight' => $weight,
'property' => $property, 'property' => $property,
'description' => htmlspecialchars($description,ENT_QUOTES) 'description' => htmlspecialchars($description,ENT_QUOTES),
'font_icon' => $font_icon
]; ];
$re = $this->db->update('on_categorys',$data,[ 'id' => $id]); $re = $this->db->update('on_categorys',$data,[ 'id' => $id]);
//var_dump( $this->db->log() ); //var_dump( $this->db->log() );
@ -417,27 +419,59 @@ class Api {
} }
/** /**
* 查询链接 * 查询链接
* 接收一个数组作为参数
*/ */
public function link_list($page,$limit,$token = ''){ public function link_list($data){
$offset = ($page - 1) * $limit; $limit = $data['limit'];
$token = $data['token'];
$offset = ($data['page'] - 1) * $data['limit'];
$fid = @$data['category_id'];
//如果存在分类ID,则根据分类ID进行查询
if ($data['category_id'] != null) {
$cid_sql = "WHERE fid = $fid";
//统计链接总数
$count = $this->db->count('on_links','*',[
'fid' => $fid
]);
}
else{
//统计链接总数,没有分类ID的情况
$count = $this->db->count('on_links','*');
}
//如果成功登录,但token为空 //如果成功登录,但token为空
if( ($this->is_login()) && (empty($token)) ){ if( ($this->is_login()) && (empty($token)) ){
//统计总数 $sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links ${cid_sql} ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
$count = $this->db->count('on_links','*');
$sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
} }
//如果token验证通过 //如果token验证通过
elseif( (!empty($token)) && ($this->auth($token)) ) { elseif( (!empty($token)) && ($this->auth($token)) ) {
//统计总数 $sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links ${cid_sql} ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
$count = $this->db->count('on_links','*');
$sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
} }
//如果即没有登录成功,又没有token,则默认为游客
else{ else{
//统计总数 $cid_sql = empty($fid) ? null : "AND fid = $fid";
if($cid_sql == null) {
//统计链接总数,不存在分类ID的情况
$count = $this->db->count('on_links','*',[ 'property' => 0 ]); $count = $this->db->count('on_links','*',[ 'property' => 0 ]);
$sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links WHERE property = 0 ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
} }
else{
//统计链接总数,存在分类ID的情况
$count = $this->db->count('on_links','*',[
'property' => 0,
'fid' => $fid
]);
}
$sql = "SELECT *,(SELECT name FROM on_categorys WHERE id = on_links.fid) AS category_name FROM on_links WHERE property = 0 ${cid_sql} ORDER BY weight DESC,id DESC LIMIT {$limit} OFFSET {$offset}";
}
//打印SQL
//echo $sql;
//如果查询的总数大于limit,则以limit为准
$count = ( $count > $limit) ? $limit : $count;
//原生查询 //原生查询
$datas = $this->db->query($sql)->fetchAll(); $datas = $this->db->query($sql)->fetchAll();
@ -449,6 +483,47 @@ class Api {
]; ];
exit(json_encode($datas)); exit(json_encode($datas));
} }
/**
* 查询单个链接
* 此函数接收一个数组
*/
public function get_a_link($data) {
$id = $data['id'];
$token = $data['token'];
$link_info = $this->db->get("on_links","*",[
"id" => $id
]);
//打印链接信息
//var_dump($link_info);
//如果是公开链接,则直接返回
if ( $link_info['property'] == "0" ) {
$datas = [
'code' => 0,
'data' => $link_info
];
}
//如果是私有链接,并且认证通过
elseif( $link_info['property'] == "1" ) {
if ( $this->auth($token) ) {
$datas = [
'code' => 0,
'data' => $link_info
];
}
//exit(json_encode($datas));
}
//如果是其它情况,则显示为空
else{
$datas = [
'code' => 0,
'data' => []
];
//exit(json_encode($datas));
}
exit(json_encode($datas));
}
/** /**
* 验证是否登录 * 验证是否登录
*/ */
@ -558,6 +633,119 @@ class Api {
$this->err_msg(-1,'Weak password!'); $this->err_msg(-1,'Weak password!');
} }
} }
/**
* 获取SQL更新列表
* 循环读取db/sql/目录下的.sql文件
*/
public function get_sql_update_list($data) {
//鉴权
if( !$this->is_login() ) {
$this->err_msg(-1002,'Authorization failure!');
}
//待更新的数据库文件目录
$sql_dir = 'db/sql/';
//待更新的sql文件列表,默认为空
$sql_files_all = [];
//打开一个目录,读取里面的文件列表
if (is_dir($sql_dir)){
if ($dh = opendir($sql_dir)){
while (($file = readdir($dh)) !== false){
//排除.和..
if ( ($file != ".") && ($file != "..") ) {
array_push($sql_files_all,$file);
}
}
//关闭句柄
closedir($dh);
}
}
//判断数据库日志表是否存在
$sql = "SELECT count(*) AS num FROM sqlite_master WHERE type='table' AND name='on_db_logs'";
//查询结果
$q_result = $this->db->query($sql)->fetchAll();
//如果数量为0,则说明on_db_logs这个表不存在,需要提前导入
$num = intval($q_result[0]['num']);
if ( $num === 0 ) {
$data = [
"code" => 0,
"data" => ['on_db_logs.sql']
];
exit(json_encode($data));
}else{
//如果不为0,则需要查询数据库更新表里面的数据进行差集比对
$get_on_db_logs = $this->db->select("on_db_logs",[
"sql_name"
],[
"status" => "TRUE"
]);
//声明一个空数组,存储已更新的数据库列表
$already_dbs = [];
foreach ($get_on_db_logs as $value) {
array_push($already_dbs,$value['sql_name']);
}
//array_diff() 函数返回两个数组的差集数组
$diff_result = array_diff($sql_files_all,$already_dbs);
//去掉键
$diff_result = array_values($diff_result);
sort($diff_result);
$data = [
"code" => 0,
"data" => $diff_result
];
exit(json_encode($data));
}
}
/**
* 执行SQL更新语句,只执行单条更新
*/
public function exe_sql($data) {
//鉴权
if( !$this->is_login() ) {
$this->err_msg(-1002,'Authorization failure!');
}
//数据库sql目录
$sql_dir = 'db/sql/';
$name = $data['name'];
$sql_name = $sql_dir.$name;
//如果文件不存在,直接返回错误
if ( !file_exists($sql_name) ) {
$this->err_msg(-2000,$name.'不存在!');
}
//读取需要更新的SQL内容
try {
$sql_content = file_get_contents($sql_name);
$result = $this->db->query($sql_content);
//如果SQL执行成功,则返回
if( $result ) {
//将更新信息写入数据库
$insert_re = $this->db->insert("on_db_logs",[
"sql_name" => $name,
"update_time" => time()
]);
if( $insert_re ) {
$data = [
"code" => 0,
"data" => $name."更新完成!"
];
exit(json_encode($data));
}
else {
$this->err_msg(-2000,$name."更新失败,请人工检查!");
}
}
else{
//如果执行失败
$this->err_msg(-2000,$name."更新失败,请人工检查!");
}
} catch(Exception $e){
$this->err_msg(-2000,$e->getMessage());
}
}
} }

52
controller/api.php

@ -54,6 +54,15 @@ switch ($method) {
case 'check_weak_password': case 'check_weak_password':
check_weak_password($api); check_weak_password($api);
break; break;
case 'get_a_link':
get_a_link($api);
break;
case 'get_sql_update_list':
get_sql_update_list($api);
break;
case 'exe_sql':
exe_sql($api);
break;
default: default:
# code... # code...
break; break;
@ -75,7 +84,9 @@ function add_category($api){
$description = empty($_POST['description']) ? '' : $_POST['description']; $description = empty($_POST['description']) ? '' : $_POST['description'];
//描述过滤 //描述过滤
$description = htmlspecialchars($description); $description = htmlspecialchars($description);
$api->add_category($token,$name,$property,$weight,$description); //获取字体图标
$font_icon = htmlspecialchars($_POST['font_icon'],ENT_QUOTES);
$api->add_category($token,$name,$property,$weight,$description,$font_icon);
} }
/** /**
* 修改分类目录入口 * 修改分类目录入口
@ -96,7 +107,9 @@ function edit_category($api){
$description = empty($_POST['description']) ? '' : $_POST['description']; $description = empty($_POST['description']) ? '' : $_POST['description'];
//描述过滤 //描述过滤
$description = htmlspecialchars($description); $description = htmlspecialchars($description);
$api->edit_category($token,$id,$name,$property,$weight,$description); //字体图标
$font_icon = htmlspecialchars($_POST['font_icon'],ENT_QUOTES);
$api->edit_category($token,$id,$name,$property,$weight,$description,$font_icon);
} }
/** /**
* 删除分类目录 * 删除分类目录
@ -173,11 +186,19 @@ function link_list($api){
$limit = empty(intval($_GET['limit'])) ? 10 : intval($_GET['limit']); $limit = empty(intval($_GET['limit'])) ? 10 : intval($_GET['limit']);
//获取token //获取token
$token = $_POST['token']; $token = $_POST['token'];
$api->link_list($page,$limit,$token); //获取分类ID
$category_id = empty($_POST['category_id']) ? null : intval($_POST['category_id']);
$data = [
'page' => $page,
'limit' => $limit,
'token' => $token,
'category_id' => $category_id
];
$api->link_list($data);
} }
/** /**
* 获取链接信息 * 获取链接标题、描述等信息
*/ */
function get_link_info($api) { function get_link_info($api) {
//获取token //获取token
@ -187,6 +208,17 @@ function get_link_info($api) {
$api->get_link_info($token,$url); $api->get_link_info($token,$url);
} }
/**
* 获取一个链接的信息,指存储在数据库的信息
*/
function get_a_link($api) {
//获取token
$data['token'] = htmlspecialchars($_POST['token']);
//获取链接的ID
$data['id'] = intval(htmlspecialchars($_GET['id']));
$api->get_a_link($data);
}
/** /**
* 添加自定义js * 添加自定义js
*/ */
@ -220,3 +252,15 @@ function check_weak_password($api) {
$token = $_POST['token']; $token = $_POST['token'];
$api->check_weak_password($token); $api->check_weak_password($token);
} }
//获取sql更新列表
function get_sql_update_list($api){
$data = [];
$api->get_sql_update_list($data);
}
//执行SQL更新
function exe_sql($api) {
$data['name'] = htmlspecialchars(trim($_GET['name']));
$api->exe_sql($data);
}

3
controller/login.php

@ -23,7 +23,8 @@ if( $_GET['check'] == 'login' ) {
header('Content-Type:application/json; charset=utf-8'); header('Content-Type:application/json; charset=utf-8');
if( ($user === $username) && ($pass === $password) ) { if( ($user === $username) && ($pass === $password) ) {
$key = md5($username.$password.'onenav'); $key = md5($username.$password.'onenav');
setcookie("key", $key, time()+30 * 24 * 60 * 60,"/"); //开启httponly支持
setcookie("key", $key, time()+30 * 24 * 60 * 60,"/",NULL,false,TRUE);
$data = [ $data = [
'code' => 0, 'code' => 0,
'msg' => 'successful' 'msg' => 'successful'

10
data/update.log

@ -38,3 +38,13 @@ CREATE INDEX on_options_key_IDX ON on_options ("key");
20220225 20220225
1. 修复一处安全漏洞 1. 修复一处安全漏洞
20220304
1. 新增HttpOnly支持
2. API新增查询单个链接信息get_a_link
3. API支持查询指定分类下的链接link_list,传递参数category_id
20220308
1. 新增数据库更新功能
2. 初始数据库更新
3. 分离分类图标字体设置

1
db/index.html

@ -0,0 +1 @@

BIN
db/onenav.simple.db3

Binary file not shown.

7
db/sql/20220308.sql

@ -0,0 +1,7 @@
-- 分类目录增加字体图标列
ALTER TABLE on_categorys ADD font_icon TEXT(32);
-- 链接表新增字段topping,默认值0(不置顶),1为置顶,先保留后续使用
ALTER TABLE on_links ADD topping INTEGER DEFAULT 0 NOT NULL;
-- 增加一个备用链接字段
ALTER TABLE on_links ADD url_standby TEXT(256);

11
db/sql/on_db_logs.sql

@ -0,0 +1,11 @@
-- 2022/03/07数据库升级脚本
-- 创建数据库升级记录表,用于新增的SQL升级成功后记录到表,方便下次比对
CREATE TABLE on_db_logs (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
sql_name TEXT(32) NOT NULL,
update_time NUMERIC NOT NULL,
status TEXT(5) DEFAULT "TRUE" NOT NULL,
extra TEXT(512),
CONSTRAINT on_db_logs_UN UNIQUE (sql_name)
);
CREATE UNIQUE INDEX on_db_logs_sql_name_IDX ON on_db_logs (sql_name);

9
templates/admin/add_category.php

@ -12,6 +12,14 @@
<input type="text" name="name" required lay-verify="required" placeholder="请输入分类名称" autocomplete="off" class="layui-input"> <input type="text" name="name" required lay-verify="required" placeholder="请输入分类名称" autocomplete="off" class="layui-input">
</div> </div>
</div> </div>
<div class="layui-form-item">
<label class="layui-form-label">字体图标</label>
<div class="layui-input-block">
<input type="text" name="font_icon" placeholder="请输入字体图标,如:fa fa-bookmark-o" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">权重</label> <label class="layui-form-label">权重</label>
<div class="layui-input-block"> <div class="layui-input-block">
@ -40,6 +48,7 @@
</div> </div>
</div> </div>
</form> </form>
<p>关于字体图标的说明请参考帮助文档:<a href="https://dwz.ovh/7nr1f" target = "_blank" title = "字体图标使用说明">https://dwz.ovh/7nr1f</a></p>
</div> </div>
</div> </div>

10
templates/admin/edit_category.php

@ -18,6 +18,14 @@
<input type="text" name="name" required lay-verify="required" value = '<?php echo $category['name']; ?>' placeholder="请输入分类名称" autocomplete="off" class="layui-input"> <input type="text" name="name" required lay-verify="required" value = '<?php echo $category['name']; ?>' placeholder="请输入分类名称" autocomplete="off" class="layui-input">
</div> </div>
</div> </div>
<div class="layui-form-item">
<label class="layui-form-label">字体图标</label>
<div class="layui-input-block">
<input type="text" name="font_icon" value = '<?php echo $category['font_icon']; ?>' placeholder="请输入字体图标,如:fa fa-bookmark-o" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">权重</label> <label class="layui-form-label">权重</label>
<div class="layui-input-block"> <div class="layui-input-block">
@ -46,9 +54,11 @@
</div> </div>
</div> </div>
</form> </form>
<p>关于字体图标的说明请参考帮助文档:<a href="https://dwz.ovh/7nr1f" target = "_blank" title = "字体图标使用说明">https://dwz.ovh/7nr1f</a></p>
</div> </div>
</div> </div>
<!-- 内容主题区域END --> <!-- 内容主题区域END -->
</div> </div>

9
templates/admin/index.php

@ -34,6 +34,14 @@
<div class="layui-col-lg4"> <div class="layui-col-lg4">
<div class = "admin-msg">捐赠地址: <a href="https://dwz.ovh/donation" rel = "nofollow" target="_blank">https://dwz.ovh/donation</a></div> <div class = "admin-msg">捐赠地址: <a href="https://dwz.ovh/donation" rel = "nofollow" target="_blank">https://dwz.ovh/donation</a></div>
</div> </div>
<!-- 日志输出窗口 -->
<div class="layui-col-lg12">
<p><h3 style = "padding-bottom:1em;">日志输出:</h3></p>
<textarea id = "console_log" name="desc" rows="20" placeholder="日志输出控制台" class="layui-textarea" readonly="readonly"></textarea>
</div>
<!-- 日志输出窗口END -->
</div> </div>
</div> </div>
</div> </div>
@ -42,4 +50,5 @@
<?php include_once('footer.php'); ?> <?php include_once('footer.php'); ?>
<script> <script>
check_weak_password(); check_weak_password();
get_sql_update_list();
</script> </script>

37
templates/admin/static/embed.js

@ -430,3 +430,40 @@ function check_weak_password(){
} }
}); });
} }
//获取待更新数据库列表,http://onenav.com/index.php?c=api&method=exe_sql&name=on_db_logs.sql
function get_sql_update_list() {
$("#console_log").append("正在检查数据库更新...\n");
$.get("index.php?c=api&method=get_sql_update_list",function(data,status){
if ( data.code == 0 ) {
//如果没有可用更新,直接结束
if ( data.data.length == 0 ) {
$("#console_log").append("当前无可用更新!\n");
return false;
}
else{
$("#console_log").append("检查到可更新SQL列表:\n");
$("#console_log").append("正在准备更新...\n");
for(i in data.data) {
sqlname = data.data[i];
//$("#console_log").append(data.data[i] + "\n");
exe_sql(sqlname);
}
}
}
});
}
//更新SQL函数
function exe_sql(sqlname) {
$.ajax({ url: "index.php?c=api&method=exe_sql&name=" + sqlname, async:false, success: function(data,status){
if( data.code == 0 ){
$("#console_log").append(sqlname + "更新完毕!\n");
}
else {
$("#console_log").append(sqlname + "更新失败!\n");
}
}});
}

7
templates/default/index.php

@ -86,11 +86,11 @@
//遍历分类目录并显示 //遍历分类目录并显示
foreach ($categorys as $category) { foreach ($categorys as $category) {
//var_dump($category); //var_dump($category);
$font_icon = empty($category['font_icon']) ? '' : "<i class='{$category['font_icon']}'></i> ";
?> ?>
<a href="#category-<?php echo $category['id']; ?>"> <a href="#category-<?php echo $category['id']; ?>">
<li class="mdui-list-item mdui-ripple"> <li class="mdui-list-item mdui-ripple">
<div class="mdui-list-item-content category-name"><?php echo htmlspecialchars_decode($category['name']); ?></div> <div class="mdui-list-item-content category-name"><?php echo $font_icon; ?><?php echo htmlspecialchars_decode($category['name']); ?></div>
</li> </li>
</a> </a>
@ -137,6 +137,7 @@
<?php foreach ( $categorys as $category ) { <?php foreach ( $categorys as $category ) {
$fid = $category['id']; $fid = $category['id'];
$links = get_links($fid); $links = get_links($fid);
$font_icon = empty($category['font_icon']) ? '' : "<i class='{$category['font_icon']}'></i> ";
//如果分类是私有的 //如果分类是私有的
if( $category['property'] == 1 ) { if( $category['property'] == 1 ) {
$property = '<i class="fa fa-expeditedssl" style = "color:#5FB878"></i>'; $property = '<i class="fa fa-expeditedssl" style = "color:#5FB878"></i>';
@ -146,6 +147,7 @@
} }
?> ?>
<div id = "category-<?php echo $category['id']; ?>" class = "mdui-col-xs-12 mdui-typo-title cat-title"> <div id = "category-<?php echo $category['id']; ?>" class = "mdui-col-xs-12 mdui-typo-title cat-title">
<?php echo $font_icon; ?>
<?php echo htmlspecialchars_decode($category['name']); ?> <?php echo $property; ?> <?php echo htmlspecialchars_decode($category['name']); ?> <?php echo $property; ?>
<span class = "mdui-typo-caption"><?php echo $category['description']; ?></span> <span class = "mdui-typo-caption"><?php echo $category['description']; ?></span>
</div> </div>
@ -194,6 +196,7 @@
<div class="mdui-divider" style = "margin-top:2em;"></div> <div class="mdui-divider" style = "margin-top:2em;"></div>
<!--正文内容部分END--> <!--正文内容部分END-->
<!-- footer部分 --> <!-- footer部分 -->
<!-- 未经作者授权,请勿去掉版权,否则可能影响作者更新代码的积极性或直接放弃维护此项目。 -->
<footer> <footer>
© 2022 Powered by <a target = "_blank" href="https://github.com/helloxz/onenav" title = "简约导航/书签管理器" rel = "nofollow">OneNav</a>.The author is <a href="https://www.xiaoz.me/" target="_blank" title = "小z博客">xiaoz.me</a> © 2022 Powered by <a target = "_blank" href="https://github.com/helloxz/onenav" title = "简约导航/书签管理器" rel = "nofollow">OneNav</a>.The author is <a href="https://www.xiaoz.me/" target="_blank" title = "小z博客">xiaoz.me</a>
</footer> </footer>

2
version.txt

@ -1 +1 @@
v0.9.15-20220225 v0.9.16-20220308
Loading…
Cancel
Save