Browse Source

Merge pull request #205 from helloxz/dev

1.0.0
main
xiaoz 5 days ago committed by GitHub
parent
commit
d1c39ebc4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 234
      class/Api.php
  2. 8
      config.simple.php
  3. 1
      controller/admin.php
  4. 36
      controller/api.php
  5. 4
      controller/index.php
  6. 29
      controller/init.php
  7. 9
      controller/login.php
  8. 32
      data/update.log
  9. BIN
      db/onenav.simple.db3
  10. 2
      db/sql/20241209.sql
  11. 13
      functions/helper.php
  12. 3
      templates/admin/add_category.php
  13. 2
      templates/admin/add_link.php
  14. 2
      templates/admin/edit_link_new.php
  15. 2
      templates/admin/footer.php
  16. 2
      templates/admin/index.php
  17. 38
      templates/admin/init.php
  18. 104
      templates/admin/link_list.php
  19. 8
      templates/admin/setting/subscribe.php
  20. 4
      templates/admin/setting/transition_page.php
  21. 66
      templates/admin/static/embed.js
  22. 3
      templates/admin/static/style.css
  23. 2
      templates/default/index.php
  24. 6
      templates/default/info.json
  25. 4
      templates/default2/assets/index.css
  26. 85
      templates/default2/assets/index.js
  27. 6
      templates/default2/info.json
  28. 2
      version.txt

234
class/Api.php

@ -1200,7 +1200,7 @@ class Api {
* 验证是否登录 * 验证是否登录
*/ */
protected function is_login(){ protected function is_login(){
$key = md5(USER.PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']); $key = md5(USER.ENCRYPTED_PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//获取session //获取session
$session = $_COOKIE['key']; $session = $_COOKIE['key'];
//如果已经成功登录 //如果已经成功登录
@ -2614,6 +2614,238 @@ class Api {
} }
} }
/**
* name: 批量检测链接
* description:check_status,0:未检测,1:正常,2:异常,3:未知(比如启用了cf)
*/
public function batch_check_links(){
set_time_limit(1200); // 设置执行最大时间为20分钟
// 验证授权
$this->auth($token);
// 验证订阅
$this->check_is_subscribe();
// 记录开始时间
$start_time = microtime(true);
// 获取所有链接
$links = $this->db->select('on_links', '*');
// 设置并发限制(最大30个并发)
$max_concurrent_requests = 30;
$multi_curl = curl_multi_init(); // 初始化curl_multi
$curl_handles = []; // 存储curl句柄
$link_count = count($links); // 获取链接数量
$completed_links = 0; // 已完成检测的链接数
// 设置curl超时时间为20分钟(1200秒)
$timeout = 10;
// 错误链接
$error_num = 0;
// 并发处理每个链接
foreach ($links as $link) {
// 创建一个curl句柄
$ch = curl_init();
// 设置curl选项
curl_setopt($ch, CURLOPT_URL, $link['url']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回内容而不是输出
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); // 设置请求超时时间
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 设置连接超时时间
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); // 跟随重定向
// 设置UA
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36");
curl_setopt($ch, CURLOPT_HTTPGET, true); // 确保使用GET请求
curl_setopt($ch, CURLOPT_HEADER, true); // 获取响应头
// 将curl句柄加入到multi句柄中
curl_multi_add_handle($multi_curl, $ch);
// 存储每个链接的ID,方便回调时更新状态
$curl_handles[$link['id']] = $ch;
}
// 执行curl请求并监听
do {
$status = curl_multi_exec($multi_curl, $active); // 执行请求
if ($status > 0) {
// 如果发生错误,输出错误信息
// echo "Curl error: " . curl_multi_strerror($status);
}
// 等待活动的请求完成
if ($active) {
curl_multi_select($multi_curl, 1); // 阻塞直到有活动的请求
}
} while ($active);
// 处理每个请求的返回结果
foreach ($curl_handles as $id => $ch) {
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 获取HTTP状态码
$error = curl_error($ch); // 获取cURL错误信息
$header = curl_multi_getcontent($ch); // 获取完整的响应内容,包括头部
// 获取Server头
preg_match('/^Server:\s*(.*)$/mi', $header, $matches);
$server_header = isset($matches[1]) ? $matches[1] : ''; // 获取Server头的值
// 获取当前时间戳
$last_checked_time = date('Y-m-d H:i:s');
// 判断链接是否有效,HTTP状态码大于400视为异常,超时或其他错误也视为异常
if ($error || $http_code >= 400) {
// 判断Server头中是否包含cloudflare和waf,不区分大小写
if (stripos($server_header, 'cloudflare') !== false || stripos($server_header, 'waf') !== false) {
$check_status = 3; // 未知
} else {
$check_status = 2; // 异常
// 错误数量+1
$error_num++;
}
} else {
$check_status = 1; // 正常
}
// 更新数据库字段
$this->db->update('on_links', [
'check_status' => $check_status,
'last_checked_time' => $last_checked_time
], ['id' => $id]);
// 删除curl句柄,释放资源
curl_multi_remove_handle($multi_curl, $ch);
curl_close($ch); // 关闭curl句柄
// 完成一个链接的检测
$completed_links++;
}
// 关闭multi句柄
curl_multi_close($multi_curl);
// 记录结束时间
$end_time = microtime(true);
// 计算总共花费的时间
$elapsed_time = $end_time - $start_time;
// 精确到s就行
$elapsed_time = round($elapsed_time, 2);
// 返回成功
$this->return_json(200, [
'completed_links' => $completed_links,
'elapsed_time' => $elapsed_time,
'error_num' => $error_num
], 'success');
}
// 获取过渡页API
public function transition_page(){
//获取当前站点信息
$transition_page = $this->db->get('on_options','value',[ 'key' => "s_transition_page" ]);
$transition_page = unserialize($transition_page);
// 返回数据
$this->return_json(200,$transition_page,'success');
}
/**
* AI检索
*/
public function ai_search() {
set_time_limit(1200); // 设置执行最大时间为20分钟
// 验证授权
$this->auth($token);
// 验证订阅
$this->check_is_subscribe();
// 获取用户输入
$content = $_GET['content'];
// 查询出所有链接,只需要url, title, description, url_standby字段
$links = $this->db->select('on_links', ['url', 'title', 'description', 'url_standby']);
// 将链接数据转换为AI需要的JSON格式
$bookmarks = [];
foreach ($links as $link) {
$bookmarks[] = [
'title' => $link['title'],
'url' => $link['url'],
'url_standby' => $link['url_standby'],
'description' => $link['description']
];
}
// 将数据转换为JSON格式
$bookmarks = json_encode($bookmarks);
// 创建AI请求的消息内容
$messages = [
[
"role" => "system",
"content" => "我会给你一段JSON格式的书签数据,其中包含每个链接的标题、URL、标签和描述等信息。你需要根据用户提供的指令或关键词,结合你所学的知识判断,并智能匹配与之相关的链接。请根据关键词的相关性来排序匹配结果,并返回匹配的链接列表以及对应的名称。
在返回匹配的链接时,确保:
1. 返回的链接与用户提供的关键词高度相关,优先匹配精确相关的内容。
2. 根据相关性将结果按从高到低排序,以确保最相关的链接出现在列表的前面。
3. 对于匹配结果,再根据你所学的知识推荐额外的5个相关链接,确保这些推荐的链接也与用户的需求相关。
例如,用户输入“AI技术”,如果书签数据中有相关的AI资源,你需要返回相关的链接列表,并根据关键词“AI”排序结果。同时,你还应该推荐额外的5个相关链接,帮助用户发现更多有价值的资源。
"
],
[
"role" => "user",
"content" => $bookmarks // 你可以根据实际需求修改用户输入
],
[
"role" => "user",
"content" => $content // 你可以根据实际需求修改用户输入
]
];
// var_dump($messages);
// 发送请求到AI接口
$response = $this->send_to_ai($bookmarks, $messages);
echo $response;
}
private function send_to_ai($bookmarks, $messages) {
// 准备请求数据
$data = [
'model' => 'qwen-plus',
'messages' => $messages,
'stream' => false
];
// 设置请求头和授权信息
$headers = [
'Content-Type: application/json',
'Authorization: Bearer sk-xxx' // 用你的实际API密钥替换
];
// 使用cURL发送请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
// 获取响应并关闭cURL
$response = curl_exec($ch);
curl_close($ch);
// var_dump($response);
// exit;
// 解析响应
return $response;
}
} }

8
config.simple.php

@ -9,12 +9,10 @@ $db = new medoo([
//用户名 //用户名
define('USER','{username}'); define('USER','{username}');
//密码 // 加密后的密码
define('PASSWORD','{password}'); define('ENCRYPTED_PASSWORD','{encrypted_password}');
//邮箱,用于后台Gravatar头像显示 //邮箱,用于后台Gravatar头像显示
define('EMAIL','{email}'); define('EMAIL','{email}');
//token参数,API需要使用,0.9.19版本这个废弃了,请通过后台设置
define('TOKEN','xiaoz.me');
//主题风格,0.9.18废弃了,请通过后台设置 //主题风格,0.9.18废弃了,请通过后台设置
define('TEMPLATE','default'); define('TEMPLATE','default');
@ -31,4 +29,4 @@ $site_setting['description'] = 'OneNav是一款使用PHP + SQLite3开发的
//这两项不要修改 //这两项不要修改
$site_setting['user'] = USER; $site_setting['user'] = USER;
$site_setting['password'] = PASSWORD; $site_setting['password'] = ENCRYPTED_PASSWORD;

1
controller/admin.php

@ -323,6 +323,7 @@ $page = $page.'.php';
function check_auth($user,$password){ function check_auth($user,$password){
if ( !is_login() ) { if ( !is_login() ) {
// exit("dsdfd");
$msg = "<h3>认证失败,请<a href = 'index.php?c=login'>重新登录</a></h3>"; $msg = "<h3>认证失败,请<a href = 'index.php?c=login'>重新登录</a></h3>";
require('templates/admin/403.php'); require('templates/admin/403.php');
exit; exit;

36
controller/api.php

@ -17,13 +17,47 @@ $api = new Api($db);
$method = $_GET['method']; $method = $_GET['method'];
//可变函数变量 //可变函数变量
$var_func = htmlspecialchars(trim($method),ENT_QUOTES); $var_func = htmlspecialchars(trim($method),ENT_QUOTES);
// 屏蔽的方法,让其不调用class/Api.php 中的方法
$deny_func = [
'__construct',
'auth',
'batch_create_category',
'check_is_subscribe',
'check_link',
'curl_get',
'deldir',
'down_updater',
'err_msg',
'general_upload',
'getData',
'getIP',
'is_login',
'is_subscribe',
'return_json',
'set_option',
'set_option_bool',
'update_link_status',
'send_to_ai'
];
// 判断是否在屏蔽列表中
if( in_array($var_func,$deny_func) ) {
exit('method not found!');
}
//判断函数是否存在,存在则条用可变函数,否则抛出错误 //判断函数是否存在,存在则条用可变函数,否则抛出错误
if ( function_exists($var_func) ) { if ( function_exists($var_func) ) {
//调用可变函数 //调用可变函数,优先调用本文件内声明的函数
$var_func($api); $var_func($api);
}else{ }else{
// 其次调用class中的函数
if( method_exists($api,$var_func) ) {
// 存在则调用
$api->$var_func();
}
else{
// 如果本文件和class/Api.php 中都不存在则抛出错误
exit('method not found!'); exit('method not found!');
} }
}

4
controller/index.php

@ -10,10 +10,12 @@ $site = unserialize($site);
$link_num = empty( $site['link_num'] ) ? 30 : intval($site['link_num']); $link_num = empty( $site['link_num'] ) ? 30 : intval($site['link_num']);
//如果已经登录,获取所有分类和链接 //如果已经登录,获取所有分类和链接
// 载入辅助函数 // 载入辅助函数
require('functions/helper.php'); require('functions/helper.php');
// 明文密码检查
unSafe();
if( is_login() ){ if( is_login() ){
//查询所有分类目录 //查询所有分类目录
$categorys = []; $categorys = [];

29
controller/init.php

@ -10,18 +10,33 @@
function check_env() { function check_env() {
//获取组件信息 //获取组件信息
$ext = get_loaded_extensions(); $ext = get_loaded_extensions();
//检查PHP版本,需要大于5.6小于8.0 //检查PHP版本,需要大于7.0小于8.0
$php_version = floatval(PHP_VERSION); $php_version = floatval(PHP_VERSION);
$uri = $_SERVER["REQUEST_URI"]; $uri = $_SERVER["REQUEST_URI"];
if( ( $php_version < 5.6 ) || ( $php_version > 8 ) ) { if( ( $php_version < 7 ) || ( $php_version > 8 ) ) {
exit("当前PHP版本{$php_version}不满足要求,需要5.6 <= PHP <= 7.4"); exit("当前PHP版本{$php_version}不满足要求,需要7.0 <= PHP <= 7.4");
} }
//检查是否支持pdo_sqlite //检查是否支持pdo_sqlite
if ( !array_search('pdo_sqlite',$ext) ) { if ( !array_search('pdo_sqlite',$ext) ) {
exit("不支持PDO_SQLITE组件,请先开启!"); exit("不支持PDO_SQLITE组件,请先开启!");
} }
if ( !array_search('openssl', $ext) ) {
exit("不支持OPENSSL组件,请先开启!");
}
//检查是否支持zlib
if ( !array_search('zlib', $ext) ) {
exit("不支持ZLIB组件,请先开启!");
}
//检查是否支持curl
if ( !array_search('curl', $ext) ) {
exit("不支持CURL组件,请先开启!");
}
//如果配置文件存在 //如果配置文件存在
if( file_exists("data/config.php") ) { if( file_exists("data/config.php") ) {
exit("配置文件已存在,无需再次初始化!"); exit("配置文件已存在,无需再次初始化!");
@ -76,6 +91,10 @@ function init($data){
if( !preg_match($p_patt,$data['password']) ) { if( !preg_match($p_patt,$data['password']) ) {
err_msg(-2000,'密码格式不正确!'); err_msg(-2000,'密码格式不正确!');
} }
// 验证邮箱是否合法
if( !filter_var($data['email'],FILTER_VALIDATE_EMAIL) ) {
err_msg(-2000,'邮箱格式不正确!');
}
$config_file = "data/config.php"; $config_file = "data/config.php";
//检查配置文件是否存在,存在则不允许设置 //检查配置文件是否存在,存在则不允许设置
if( file_exists($config_file) ) { if( file_exists($config_file) ) {
@ -88,7 +107,9 @@ function init($data){
//替换内容 //替换内容
$content = str_replace('{email}',$data['email'],$content); $content = str_replace('{email}',$data['email'],$content);
$content = str_replace('{username}',$data['username'],$content); $content = str_replace('{username}',$data['username'],$content);
$content = str_replace('{password}',$data['password'],$content); // $content = str_replace('{password}',$data['password'],$content);
// 存入加密后的密码,用户名 + 密码,再进行MD5加密
$content = str_replace('{encrypted_password}',md5($data['username'].$data['password']),$content);
//写入配置文件 //写入配置文件
if( !file_put_contents($config_file,$content) ) { if( !file_put_contents($config_file,$content) ) {

9
controller/login.php

@ -7,7 +7,8 @@
require('functions/helper.php'); require('functions/helper.php');
$username = $site_setting['user']; $username = $site_setting['user'];
$password = $site_setting['password']; // 加密后的密码
$password = ENCRYPTED_PASSWORD;
$ip = getIP(); $ip = getIP();
//如果认证通过,直接跳转到后台管理 //如果认证通过,直接跳转到后台管理
$key = md5($username.$password.'onenav'.$_SERVER['HTTP_USER_AGENT']); $key = md5($username.$password.'onenav'.$_SERVER['HTTP_USER_AGENT']);
@ -25,8 +26,10 @@ if( is_login() ){
//登录检查 //登录检查
if( $_GET['check'] == 'login' ) { if( $_GET['check'] == 'login' ) {
$user = $_POST['user']; $user = trim($_POST['user']);
$pass = $_POST['password']; $pass = trim($_POST['password']);
// 用户密码进行加密处理,加密算法为用户名 + 密码,再进行MD5加密
$pass = md5($user.$pass);
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'.$_SERVER['HTTP_USER_AGENT']); $key = md5($username.$password.'onenav'.$_SERVER['HTTP_USER_AGENT']);

32
data/update.log

@ -1,3 +1,35 @@
2024.12.17
1. 修改数据库初始化数据
2024.12.13
1. 新增:全新过渡页
2. 去掉后台过渡页设置中的自定义菜单
2024.12.11
1. 优化链接图标上传,避免重复问题
2. Docker用户可以将 favicon.ico 放置在 /data 目录下,从而避免了网站图标被覆盖问题
3. 优化初始化界面
2024.12.10
1. 新增批量检测功能和接口:batch_check_links
2. 优化API方法,现在可自动获取 Api.php 中的对象方法
2024.12.06
1. 修复编辑链接不支持IPV6的问题
2. 修改PHP版本检测,不再支持PHP 5.6
3. 初始化是新增:openssl/zlib/curl扩展检测
4. 修改默认主题底部版权信息
5. 修改管理后台底部版权信息展示
6. 密码加密处理
2024.12.04
1. 优化default2主题平板显示问题
2. default2主题添加常用搜索引擎
3. default2主题搜索框支持ESC取消输入
4. default2主题新增刷新分类
5. 优化了一些样式
2024.11.27 2024.11.27
1. 修改默认主题为`default2` 1. 修改默认主题为`default2`
2. 后端禁止删除默认主题:default2 2. 后端禁止删除默认主题:default2

BIN
db/onenav.simple.db3

Binary file not shown.

2
db/sql/20241209.sql

@ -0,0 +1,2 @@
ALTER TABLE on_links ADD check_status INTEGER DEFAULT (0) NOT NULL;
ALTER TABLE on_links ADD last_checked_time TEXT;

13
functions/helper.php

@ -24,7 +24,7 @@ function getIP() {
function is_login(){ function is_login(){
$key = md5(USER.PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']); $key = md5(USER.ENCRYPTED_PASSWORD.'onenav'.$_SERVER['HTTP_USER_AGENT']);
//获取session //获取session
$session = $_COOKIE['key']; $session = $_COOKIE['key'];
//如果已经成功登录 //如果已经成功登录
@ -226,3 +226,14 @@ function check_all_cat(){
} }
} }
/**
* name:检查是否存在明文密码参数,如果存在,则提示重新初始化
*/
function unSafe() {
$password = PASSWORD;
if( isset($password) && $password !== 'PASSWORD' ) {
exit("由于安全升级,请删除站点目录下的 data/config.php 文件后,重新完成初始化!");
}
}

3
templates/admin/add_category.php

@ -7,8 +7,7 @@
<!-- 说明提示框 --> <!-- 说明提示框 -->
<div class="layui-col-lg12"> <div class="layui-col-lg12">
<div class="setting-msg"> <div class="setting-msg">
<p>1. 关于字体图标的说明请参考帮助文档:<a href="https://dwz.ovh/7nr1f" target = "_blank" title = "字体图标使用说明">https://dwz.ovh/7nr1f</a></p> <p>注意:权重越大,分类排序越靠前</p>
<p>2. 权重越大,排序越靠前</p>
</div> </div>
</div> </div>
<!-- 说明提示框END --> <!-- 说明提示框END -->

2
templates/admin/add_link.php

@ -9,7 +9,7 @@
<div class="setting-msg"> <div class="setting-msg">
<p>1. 权重越大,排序越靠前</p> <p>1. 权重越大,排序越靠前</p>
<p>2. 识别功能可以自动获取链接标题和描述信息,但不确保一定成功</p> <p>2. 识别功能可以自动获取链接标题和描述信息,但不确保一定成功</p>
<p>3. 仅 5iux/heimdall/tushan2/webstack 支持自定义图标,其余主题均自动获取链接图标。</p> <p>3. 仅 default2/5iux/heimdall/tushan2/webstack 支持自定义图标,其余主题均自动获取链接图标。</p>
</div> </div>
</div> </div>
<!-- 说明提示框END --> <!-- 说明提示框END -->

2
templates/admin/edit_link_new.php

@ -22,7 +22,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">URL</label> <label class="layui-form-label">URL</label>
<div class="layui-input-block"> <div class="layui-input-block">
<input type="url" id = "url" name="url" value = "<?php echo $link['url']; ?>" required lay-verify="required|url" placeholder="请输入有效链接" autocomplete="off" class="layui-input"> <input type="url" id = "url" name="url" value = "<?php echo $link['url']; ?>" required lay-verify="required" placeholder="请输入有效链接" autocomplete="off" class="layui-input">
</div> </div>
</div> </div>

2
templates/admin/footer.php

@ -1,6 +1,6 @@
<div class="layui-footer"> <div class="layui-footer">
<!-- 底部固定区域 --> <!-- 底部固定区域 -->
© Copyright 2024.Powered by <a href="https://www.onenav.top/" rel = "nofollow" target="_blank">OneNav</a>. © Copyright <?php echo date("Y"); ?>.Powered by <a href="https://www.onenav.top/" rel = "nofollow" target="_blank">OneNav</a>.
</div> </div>
</div> </div>
</body> </body>

2
templates/admin/index.php

@ -107,7 +107,7 @@
<div class="layui-col-lg3"> <div class="layui-col-lg3">
<div class = "admin-msg"> <div class = "admin-msg">
<h2>Chrome浏览器扩展</h2> <h2>浏览器扩展</h2>
<p class="text"> <p class="text">
<a href="https://dwz.ovh/4kxn2" title = "适用于Chromium内核的浏览器扩展" rel = "nofollow" target="_blank">https://dwz.ovh/4kxn2</a> <a href="https://dwz.ovh/4kxn2" title = "适用于Chromium内核的浏览器扩展" rel = "nofollow" target="_blank">https://dwz.ovh/4kxn2</a>
</p> </p>

38
templates/admin/init.php

@ -13,8 +13,21 @@
<style> <style>
body{ body{
/* background:url(templates/admin/static/bg.jpg); */ /* background:url(templates/admin/static/bg.jpg); */
background-color:rgba(0, 0, 51, 0.8); background: linear-gradient(to right, #a8c0ff, #3f2b96);
color: #333333;
}
.box{
padding:18px;
background-color:#fff;
border-radius:12px;
color: #333333;
/** 阴影特效 */
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.box-title{
text-align:center;
color: #2f363c;
padding:18px 0 30px 0;
} }
</style> </style>
@ -23,10 +36,13 @@
<div class="layui-container"> <div class="layui-container">
<div class="layui-row"> <div class="layui-row">
<div class="login-logo">
<h1>初始化OneNav用户名/密码</h1>
</div>
<div class="layui-col-lg4 layui-col-md-offset4" style ="margin-top:4em;"> <div class="layui-col-lg4 layui-col-md-offset4" style ="margin-top:4em;">
<!-- star box -->
<div class="box">
<div class="box-title">
<h2>初始化OneNav</h2>
</div>
<form class="layui-form layui-form-pane" action=""> <form class="layui-form layui-form-pane" action="">
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">用户名</label> <label class="layui-form-label">用户名</label>
@ -34,6 +50,7 @@
<input type="text" name="username" required lay-verify="required" placeholder="3-32位的字母或数字" autocomplete="off" class="layui-input"> <input type="text" name="username" required lay-verify="required" placeholder="3-32位的字母或数字" autocomplete="off" class="layui-input">
</div> </div>
</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">
@ -49,11 +66,20 @@
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="init_onenav" style = "width:100%;">设置</button> <label class="layui-form-label">邮箱</label>
<div class="layui-input-block">
<input type="text" name="email" required lay-verify="required|email" placeholder="请输入您的常用邮箱" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="init_onenav" style = "width:100%;">完成初始化</button>
</div> </div>
</form> </form>
</div> </div>
<!-- end box -->
</div>
</div> </div>
</div> </div>

104
templates/admin/link_list.php

@ -9,9 +9,9 @@
<div class="layui-col-lg12"> <div class="layui-col-lg12">
<div class="page-msg"> <div class="page-msg">
<ol> <ol>
<li>仅 5iux/heimdall/tushan2/webstack 支持自定义图标,其余主题均自动获取链接图标。</li> <li>default2/5iux/heimdall/tushan2/webstack 支持自定义图标,其余主题均自动获取链接图标。</li>
<li>分类的私有属性优先级高于链接的私有属性</li> <li>分类的私有属性优先级高于链接的私有属性</li>
<li>权重数字越大,排序越靠前</li> <li>权重数字越大,链接排序越靠前</li>
</ol> </ol>
</div> </div>
</div> </div>
@ -39,16 +39,24 @@
<div style="width:50px;display: inline-block;"></div> <div style="width:50px;display: inline-block;"></div>
<!-- 顶部搜索 --> <!-- 顶部搜索 -->
<div class="layui-inline"> <div class="layui-inline" style="border-right:1px solid #ccc;">
<div class="layui-input-inline"> <div class="layui-input-inline">
<input type="text" name="keyword" id="keyword" placeholder="请输入关键词" autocomplete="off" class="layui-input"> <input type="text" name="keyword" id="keyword" placeholder="请输入关键词" autocomplete="off" class="layui-input">
</div> </div>
<div class="layui-input-inline" style="width: 100px;"> <div class="layui-input-inline" style="width: 70px;">
<button class="layui-btn" lay-submit lay-filter="search_keyword">搜索</button> <button class="layui-btn" lay-submit lay-filter="search_keyword">搜索</button>
</div> </div>
</div> </div>
<!-- 顶部搜索END --> <!-- 顶部搜索END -->
<!-- 批量检测 -->
<div class="layui-inline">
<div class="layui-input-inline">
<button class="layui-btn" lay-submit lay-filter="batch_check">批量检测</button>
</div>
</div>
<!-- 批量检测END -->
</div> </div>
</form> </form>
</div> </div>
@ -128,6 +136,59 @@ layui.use(['table','form'], function(){
}); });
}); });
// 提交批量检测
form.on('submit(batch_check)', function(data){
let content = `
<ul>
<li>1. 此功能仅订阅用户可用!</li>
<li>2. 检测较为耗时,检测期间请勿关闭和刷新此页面!</li>
<li>3. 检测结果仅供参考,无法确保100%准确,具体以实际访问为准!</li>
</ul>
`;
// 弹出确认提示
layer.confirm(content, {
btn: ['开始检测','取消'],
title: '即将对链接进行批量检测!',
area: ['400px', 'auto']
}, function(){
// 关闭确认提示
layer.closeAll('dialog');
// 显示全局加载
var index = layer.load(1);
$.ajax({
url: '/index.php?c=api&method=batch_check_links',
type: 'GET',
success: function(response) {
// 请求成功后执行的代码
if( response.code == 200 ) {
// 关闭全局加载
layer.close(index);
layer.msg("批量检测成功!",{icon:1});
// 2s后重新载入页面
setTimeout(function(){
location.reload();
},2000);
}
else{
layer.msg(response.msg,{icon:5});
// 关闭全局加载
layer.close(index);
}
},
error: function(xhr, status, error) {
layer.close(index);
// 请求出错时执行的代码
console.log(error);
layer.msg("批量检测失败!",{icon:5});
}
});
}, function(){
// 取消后执行的代码
});
return false;
});
// 提交搜索 // 提交搜索
form.on('submit(search_keyword)', function(data){ form.on('submit(search_keyword)', function(data){
console.log(data.field); console.log(data.field);
@ -181,7 +242,21 @@ layui.use(['table','form'], function(){
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'} ,{field: 'check_status', title: '状态', width: 80,sort:true,templet:function(d){
let title = `检测时间:${d.last_checked_time}`;
if(d.check_status == 1) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-green">正常</span>`;
}
else if(d.check_status == 2) {
return `<span title="${title}" class="link-status-text layui-badge">异常</span>`;
}
else if(d.check_status == 3) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-cyan">未知</span>`;
}
else {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-gray">未检测</span>`;
}
}}
,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){ ,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){
if(d.property == 1) { if(d.property == 1) {
return '<button type="button" class="layui-btn layui-btn-xs"></button>'; return '<button type="button" class="layui-btn layui-btn-xs"></button>';
@ -190,6 +265,7 @@ layui.use(['table','form'], function(){
return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger"></button>'; return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger"></button>';
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'}
,{field: 'click', title: '点击数',width:90,sort:true} ,{field: 'click', title: '点击数',width:90,sort:true}
,{fixed: 'right', title:'操作', toolbar: '#link_operate'} ,{fixed: 'right', title:'操作', toolbar: '#link_operate'}
]] ]]
@ -252,7 +328,21 @@ function reset_query(){
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'} ,{field: 'check_status', title: '状态', width: 80,sort:true,templet:function(d){
let title = `检测时间:${d.last_checked_time}`;
if(d.check_status == 1) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-green">正常</span>`;
}
else if(d.check_status == 2) {
return `<span title="${title}" class="link-status-text layui-badge">异常</span>`;
}
else if(d.check_status == 3) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-cyan">未知</span>`;
}
else {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-gray">未检测</span>`;
}
}}
,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){ ,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){
if(d.property == 1) { if(d.property == 1) {
return '<button type="button" class="layui-btn layui-btn-xs"></button>'; return '<button type="button" class="layui-btn layui-btn-xs"></button>';
@ -261,6 +351,7 @@ function reset_query(){
return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger"></button>'; return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger"></button>';
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'}
,{field: 'click', title: '点击数',width:90,sort:true} ,{field: 'click', title: '点击数',width:90,sort:true}
,{fixed: 'right', title:'操作', toolbar: '#link_operate'} ,{fixed: 'right', title:'操作', toolbar: '#link_operate'}
]] ]]
@ -270,5 +361,6 @@ function reset_query(){
} }
</script> </script>
<?php include_once('footer.php'); ?> <?php include_once('footer.php'); ?>

8
templates/admin/setting/subscribe.php

@ -61,7 +61,7 @@
<div class="layui-form-item"> <div class="layui-form-item">
<button class="layui-btn" lay-submit="" lay-filter="set_subscribe">保存设置</button> <button class="layui-btn" lay-submit="" lay-filter="set_subscribe">保存设置</button>
<button class="layui-btn" lay-submit="" lay-filter="reset_subscribe">删除订阅</button> <button class="layui-btn" lay-submit="" lay-filter="reset_subscribe">删除订阅</button>
<a class="layui-btn layui-btn-danger" rel = "nofollow" target = "_blank" title = "点此购买订阅" href="https://dwz.ovh/69h9q"><i class="fa fa-shopping-cart"></i> 购买订阅</a> <a class="layui-btn layui-btn-danger" onclick="buySubscribe('<?php echo get_host(); ?>')" rel = "nofollow" title = "点此购买订阅" href="javascript:;"><i class="fa fa-shopping-cart"></i> 购买订阅</a>
</div> </div>
</form> </form>
@ -121,6 +121,12 @@
<script> <script>
// 购买订阅
function buySubscribe(url) {
// 新窗口打开购买订阅页面
window.open("https://shop.xiuping.net/onenav/index?domain=" + url);
}
//获取可更新版本 //获取可更新版本
function available_version() { function available_version() {
var current_version = $("#current_version").val(); var current_version = $("#current_version").val();

4
templates/admin/setting/transition_page.php

@ -39,12 +39,12 @@
<div class="layui-form-mid layui-word-aux">管理员停留时间,单位秒</div> <div class="layui-form-mid layui-word-aux">管理员停留时间,单位秒</div>
</div> </div>
<div class="layui-form-item layui-form-text"> <!-- <div class="layui-form-item layui-form-text">
<label class="layui-form-label">过渡页菜单(订阅可用)</label> <label class="layui-form-label">过渡页菜单(订阅可用)</label>
<div class="layui-input-block"> <div class="layui-input-block">
<textarea name = "menu" placeholder="请参考帮助文档进行设置!" rows = "4" class="layui-textarea"><?php echo $transition_page['menu']; ?></textarea> <textarea name = "menu" placeholder="请参考帮助文档进行设置!" rows = "4" class="layui-textarea"><?php echo $transition_page['menu']; ?></textarea>
</div> </div>
</div> </div> -->
<!-- <div class="layui-form-item layui-form-text"> <!-- <div class="layui-form-item layui-form-text">
<label class="layui-form-label">自定义footer,支持HTML(订阅可用)</label> <label class="layui-form-label">自定义footer,支持HTML(订阅可用)</label>

66
templates/admin/static/embed.js

@ -42,31 +42,8 @@ function set_icon_name(){
//获取icon名称 //获取icon名称
function get_icon_name(){ function get_icon_name(){
let icon_name; // console.log('this?');
//从表单获取 return getRandomString(6);
let tmp_name = $("#font_icon").val();
if( tmp_name == undefined ) {
return false;
}
tmp_name = tmp_name.split("/");
tmp_name = tmp_name.pop();
tmp_name = tmp_name.split(".");
tmp_name = tmp_name[0];
icon_name = tmp_name;
//如果不存在,则从session获取
if( icon_name == "" || icon_name == undefined ) {
icon_name = sessionStorage.icon_name;
}
//如果session也不存在,则重新设置一个
if( icon_name == "" || icon_name == undefined ) {
set_icon_name();
icon_name = sessionStorage.icon_name;
}
//最后返回
return icon_name;
} }
//获取老图标的完整路径 //获取老图标的完整路径
@ -210,7 +187,21 @@ layui.use(['element','table','layer','form','upload','iconHhysFa'], function(){
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'} ,{field: 'check_status', title: '状态', width: 80,sort:true,templet:function(d){
let title = `检测时间:${d.last_checked_time}`;
if(d.check_status == 1) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-green">正常</span>`;
}
else if(d.check_status == 2) {
return `<span title="${title}" class="link-status-text layui-badge">异常</span>`;
}
else if(d.check_status == 3) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-cyan">未知</span>`;
}
else {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-gray">未检测</span>`;
}
}}
,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){ ,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){
if(d.property == 1) { if(d.property == 1) {
return '<button type="button" class="layui-btn layui-btn-xs">是</button>'; return '<button type="button" class="layui-btn layui-btn-xs">是</button>';
@ -219,6 +210,7 @@ layui.use(['element','table','layer','form','upload','iconHhysFa'], function(){
return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">否</button>'; return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">否</button>';
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'}
,{field: 'click', title: '点击数',width:90,sort:true} ,{field: 'click', title: '点击数',width:90,sort:true}
,{fixed: 'right', title:'操作', toolbar: '#link_operate'} ,{fixed: 'right', title:'操作', toolbar: '#link_operate'}
]] ]]
@ -547,7 +539,21 @@ layui.use(['element','table','layer','form','upload','iconHhysFa'], function(){
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'} ,{field: 'check_status', title: '状态', width: 80,sort:true,templet:function(d){
let title = `检测时间:${d.last_checked_time}`;
if(d.check_status == 1) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-green">正常</span>`;
}
else if(d.check_status == 2) {
return `<span title="${title}" class="link-status-text layui-badge">异常</span>`;
}
else if(d.check_status == 3) {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-cyan">未知</span>`;
}
else {
return `<span title="${title}" class="link-status-text layui-badge layui-bg-gray">未检测</span>`;
}
}}
,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){ ,{field: 'property', title: '私有', width: 80, sort: true,templet: function(d){
if(d.property == 1) { if(d.property == 1) {
return '<button type="button" class="layui-btn layui-btn-xs">是</button>'; return '<button type="button" class="layui-btn layui-btn-xs">是</button>';
@ -556,6 +562,7 @@ layui.use(['element','table','layer','form','upload','iconHhysFa'], function(){
return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">否</button>'; return '<button type="button" class="layui-btn layui-btn-xs layui-btn-danger">否</button>';
} }
}} }}
,{field: 'weight', title: '权重', width: 75,sort:true,edit: 'text'}
,{field: 'click', title: '点击数',width:90,sort:true} ,{field: 'click', title: '点击数',width:90,sort:true}
,{fixed: 'right', title:'操作', toolbar: '#link_operate'} ,{fixed: 'right', title:'操作', toolbar: '#link_operate'}
]] ]]
@ -829,7 +836,7 @@ layui.use(['element','table','layer','form','upload','iconHhysFa'], function(){
layer.msg(data.err_msg, {icon: 5}); layer.msg(data.err_msg, {icon: 5});
} }
}); });
console.log(data.field) //当前容器的全部表单字段,名值对形式:{name: value} // console.log(data.field) //当前容器的全部表单字段,名值对形式:{name: value}
return false; //阻止表单跳转。如果需要表单跳转,去掉这段即可。 return false; //阻止表单跳转。如果需要表单跳转,去掉这段即可。
}); });
//识别链接信息 //识别链接信息
@ -1009,7 +1016,8 @@ function check_db_down(){
url:"/data/onenav.db3", url:"/data/onenav.db3",
statusCode: { statusCode: {
200: function() { 200: function() {
$("#console_log").append("危险!!!危险!!!危险!!!数据库可被下载,请尽快参考帮助文档:https://dwz.ovh/jvr2t 加固安全设置!<br /><br />"); let msg = `危险!!!危险!!!危险!!!数据库可被下载,请尽快参考帮助文档:<a target = "_blank" href = "https://dwz.ovh/jvr2t">https://dwz.ovh/jvr2t</a> 加固安全设置!<br /><br />`;
$("#console_log").append(msg);
}, },
403:function() { 403:function() {
$("#console_log").append("您的数据库看起来是安全的!<br />"); $("#console_log").append("您的数据库看起来是安全的!<br />");

3
templates/admin/static/style.css

@ -240,3 +240,6 @@
text-align: right; text-align: right;
float:right; float:right;
} }
.link-status-text{
cursor: pointer;
}

2
templates/default/index.php

@ -273,7 +273,7 @@
<!-- 未经作者授权,请勿去掉版权,否则可能影响作者更新代码的积极性或直接放弃维护此项目。 --> <!-- 未经作者授权,请勿去掉版权,否则可能影响作者更新代码的积极性或直接放弃维护此项目。 -->
<footer> <footer>
<?php if(empty( $site['custom_footer']) ){ ?> <?php if(empty( $site['custom_footer']) ){ ?>
© 2023 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> © <?php echo date("Y"); ?> Powered by <a target = "_blank" href="https://www.onenav.top/" title = "免费开源的书签管理工具" rel = "nofollow">OneNav</a>.
<?php }else{ <?php }else{
echo $site['custom_footer']; echo $site['custom_footer'];
} ?> } ?>

6
templates/default/info.json

@ -1,10 +1,10 @@
{ {
"name": "OneNav默认主题", "name": "OneNav默认主题",
"description": "OneNav默认主题", "description": "OneNav默认主题",
"homepage": "https:\/\/www.xiaoz.me", "homepage": "https:\/\/blog.xiaoz.org",
"help_url":"https://dwz.ovh/gnae4", "help_url":"https://dwz.ovh/gnae4",
"version": "0.9.31", "version": "0.9.32",
"update": "2023\/06\/28", "update": "2024\/12\/17",
"author": "xiaoz<xiaoz93@outlook.com>", "author": "xiaoz<xiaoz93@outlook.com>",
"screenshot": "https:\/\/img.rss.ink\/imgs\/2022\/03\/42ed3ef2c4a50f6d.png", "screenshot": "https:\/\/img.rss.ink\/imgs\/2022\/03\/42ed3ef2c4a50f6d.png",
"demo":"", "demo":"",

4
templates/default2/assets/index.css

File diff suppressed because one or more lines are too long

85
templates/default2/assets/index.js

File diff suppressed because one or more lines are too long

6
templates/default2/info.json

@ -3,13 +3,13 @@
"description": "OneNav目前功能最强大的默认主题,推荐使用。", "description": "OneNav目前功能最强大的默认主题,推荐使用。",
"homepage": "https:\/\/blog.xiaoz.org", "homepage": "https:\/\/blog.xiaoz.org",
"help_url":"https://dwz.ovh/gnae4", "help_url":"https://dwz.ovh/gnae4",
"version": "1.0.2", "version": "1.1.0",
"update": "2024\/11\/28", "update": "2024\/12\/17",
"author": "xiaoz<xiaoz93@outlook.com>", "author": "xiaoz<xiaoz93@outlook.com>",
"screenshot": "https://v.png.pub/imgs/2024/11/27/c01894e5d9e0d850.png", "screenshot": "https://v.png.pub/imgs/2024/11/27/c01894e5d9e0d850.png",
"demo":"https://nav.rss.ink", "demo":"https://nav.rss.ink",
"require":{ "require":{
"min":"0.9.36", "min":"1.0.0",
"max":"" "max":""
}, },
"config": { "config": {

2
version.txt

@ -1 +1 @@
v0.9.37-20241128 v1.0.0-20241217
Loading…
Cancel
Save