使用PHP开发的简约导航/书签管理系统。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

824 lines
22 KiB

<?php
/**
* name:API入口文件,也可以称之为中间件
* update:2022/03
* author:xiaoz<xiaoz93@outlook.com>
* blog:xiaoz.me
*/
//允许跨域访问
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Access-Control-Allow-Private-Network,Content-Type, AccessToken, X-CSRF-Token, Authorization, Token,X-Token,X-Cid");
require('./class/Api.php');
/**
* 读取请求参数,缺失时返回默认值
* 避免 PHP 8 下未定义索引和 null 传入字符串函数的弃用警告
*/
function req_param($key, $default = '')
{
if ( array_key_exists($key, $_POST) ) {
return $_POST[$key];
}
if ( array_key_exists($key, $_GET) ) {
return $_GET[$key];
}
if ( array_key_exists($key, $_REQUEST) ) {
return $_REQUEST[$key];
}
return $default;
}
/**
* 获取 API token,POST 优先,其次 GET
*/
function req_token()
{
if ( !empty($_POST['token']) ) {
return $_POST['token'];
}
if ( isset($_GET['token']) ) {
return $_GET['token'];
}
return '';
}
$api = new Api($db);
//获取请求方法
$method = $_GET['method'] ?? '';
//可变函数变量
$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',
'get_options',
'req_param',
'req_token'
];
// 判断是否在屏蔽列表中
if( in_array($var_func,$deny_func) ) {
exit('method not found!');
}
//判断函数是否存在,存在则条用可变函数,否则抛出错误
if ( function_exists($var_func) ) {
//调用可变函数,优先调用本文件内声明的函数
$var_func($api);
}else{
// 其次调用class中的函数
if( method_exists($api,$var_func) ) {
// 存在则调用
$api->$var_func();
}
else{
// 如果本文件和class/Api.php 中都不存在则抛出错误
exit('method not found!');
}
}
/**
* 添加分类目录入口
*/
function add_category($api){
//获取token
$token = req_token();
//获取分类名称
$name = req_param('name', '');
//获取私有属性
$property = empty($_POST['property']) ? 0 : 1;
//获取分级ID
$fid = intval(req_param('fid', ''));
//获取权重
$weight = empty($_POST['weight']) ? 0 : intval(req_param('weight', ''));
//获取描述
$description = empty($_POST['description']) ? '' : req_param('description', '');
//描述过滤
$description = htmlspecialchars($description);
//获取字体图标
$font_icon = htmlspecialchars(req_param('font_icon', ''),ENT_QUOTES);
//搜索字体图标是否包含'fa ',如果不包含则自动加上
if( !strstr($font_icon,'fa ') ) {
$font_icon = 'fa '.$font_icon;
}
$api->add_category($token,$name,$property,$weight,$description,$font_icon,$fid);
}
/**
* 修改分类目录入口
*/
function edit_category($api){
//获取ID
$id = intval(req_param('id', ''));
//获取父级ID
$fid = intval(req_param('fid', ''));
//获取token
$token = req_token();
//获取分类名称
$name = req_param('name', '');
//获取私有属性
$property = empty($_POST['property']) ? 0 : 1;
//获取权重
$weight = empty($_POST['weight']) ? 0 : intval(req_param('weight', ''));
//获取描述
$description = empty($_POST['description']) ? '' : req_param('description', '');
//描述过滤
$description = htmlspecialchars($description);
//字体图标
$font_icon = htmlspecialchars(req_param('font_icon', ''),ENT_QUOTES);
//搜索字体图标是否包含'fa ',如果不包含则自动加上
if( !strstr($font_icon,'fa ') ) {
$font_icon = 'fa '.$font_icon;
}
$api->edit_category($token,$id,$name,$property,$weight,$description,$font_icon,$fid);
}
/**
* 删除分类目录
*/
function del_category($api){
//获取ID
$id = intval(req_param('id', ''));
//获取token
$token = req_token();
$api->del_category($token,$id);
}
/**
* 插入链接
*/
function add_link($api){
//add_link($token,$fid,$title,$url,$description = '',$weight = 0,$property = 0)
//获取token
$token = req_token();
//获取fid
$fid = intval(req_param('fid', ''));
$title = req_param('title', '');
$url = req_param('url', '');
$url_standby = req_param('url_standby', '');
$description = empty($_POST['description']) ? '' : req_param('description', '');
$weight = empty($_POST['weight']) ? 0 : intval(req_param('weight', ''));
$property = empty($_POST['property']) ? 0 : 1;
$font_icon = empty($_POST['font_icon']) ? '' : req_param('font_icon', '');
$api->add_link($token,$fid,$title,$url,$description,$weight,$property,$url_standby,$font_icon);
}
/**
* 修改链接
*/
function edit_link($api){
//add_link($token,$fid,$title,$url,$description = '',$weight = 0,$property = 0)
//获取token
$token = req_token();
$id = intval(req_param('id', ''));
//获取fid
$fid = intval(req_param('fid', ''));
$title = req_param('title', '');
$url = req_param('url', '');
$url_standby = req_param('url_standby', '');
$description = empty($_POST['description']) ? '' : req_param('description', '');
$weight = empty($_POST['weight']) ? 0 : intval(req_param('weight', ''));
$property = empty($_POST['property']) ? 0 : 1;
$font_icon = empty($_POST['font_icon']) ? '' : req_param('font_icon', '');
$api->edit_link($token,$id,$fid,$title,$url,$description,$weight,$property,$url_standby,$font_icon);
}
/**
* 删除链接
*/
function del_link($api){
$token = req_token();
$id = intval(req_param('id', ''));
$api->del_link($token,$id);
}
/**
* 查询分类目录列表
*/
function category_list($api){
$page = empty(intval(req_param('page', ''))) ? 1 : intval(req_param('page', ''));
$limit = empty(intval(req_param('limit', ''))) ? 10 : intval(req_param('limit', ''));
$api->category_list($page,$limit);
}
/**
* 查询链接列表
*/
function link_list($api){
$page = empty(intval(req_param('page', ''))) ? 1 : intval(req_param('page', ''));
$limit = empty(intval(req_param('limit', ''))) ? 10 : intval(req_param('limit', ''));
//获取token
$token = req_token();
//获取分类ID
$category_id = empty($_POST['category_id']) ? null : intval(req_param('category_id', ''));
$data = [
'page' => $page,
'limit' => $limit,
'token' => $token,
'category_id' => $category_id
];
$api->link_list($data);
}
/**
* 查询分类下的链接
*/
function q_category_link($api){
$page = empty(intval(req_param('page', ''))) ? 1 : intval(req_param('page', ''));
$limit = empty(intval(req_param('limit', ''))) ? 10 : intval(req_param('limit', ''));
//获取token
$token = req_token();
//获取分类ID
$category_id = empty($_REQUEST['category_id']) ? null : intval(req_param('category_id', ''));
$data = [
'page' => $page,
'limit' => $limit,
'token' => $token,
'category_id' => $category_id
];
$api->q_category_link($data);
}
/**
* 获取链接标题、描述等信息
*/
function get_link_info($api) {
//获取token
$token = req_token();
//获取URL
$url = req_param('url', '');
$api->get_link_info($token,$url);
}
/**
* 根据ID获取单个分类信息
*/
function get_a_category($api) {
//获取token
$data['token'] = req_token();
//获取分类ID
$data['id'] = intval(trim(req_param('id', '')));
//var_dump($data);
$api->get_a_category($data);
}
/**
* 获取一个链接的信息,指存储在数据库的信息
*/
function get_a_link($api) {
//获取token
$data['token'] = htmlspecialchars(req_token());
//获取链接的ID
$data['id'] = intval(htmlspecialchars(req_param('id', '')));
$api->get_a_link($data);
}
/**
* 添加自定义js
*/
function add_js($api) {
//获取token
$token = req_token();
$content = req_param('content', '');
$api->add_js($token,$content);
}
// 上传书签
function upload($api){
//获取token
$token = req_token();
//获取上传类型
$type = req_param('type', '');
$api->upload($token,$type);
}
// 上传图标
function uploadImages(){
global $api;
//获取token
$token = req_token();
//获取上传类型
$api->uploadImages($token);
}
//书签导入
function imp_link($api) {
//获取token
$token = req_token();
//获取书签路径
$filename = trim(req_param('filename', ''));
$fid = intval(req_param('fid', ''));
$property = intval(req_param('property', ''));
$api->imp_link($token,$filename,$fid,$property);
}
//新版书签批量导入并自动创建分类
function import_link($api) {
//获取token
$token = req_token();
//获取书签路径
$filename = trim(req_param('filename', ''));
$fid = intval(req_param('fid', ''));
$property = intval(req_param('property', ''));
$api->import_link($filename,$property);
}
//检查弱密码
function check_weak_password($api) {
//获取token
$token = req_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(req_param('name', '')));
$api->exe_sql($data);
}
//设置options表
function set_theme($api) {
$key = 's_themes';
$value = req_param('value', '');
$api->set_option($key,$value);
}
//设置站点信息
function set_site($api) {
//获取传递过来的参数
//获取网站标题
$data['title'] = htmlspecialchars(req_param('title', ''));
//获取网站logo
$data['logo'] = htmlspecialchars(req_param('logo', ''));
//获取副标题
$data['subtitle'] = htmlspecialchars(req_param('subtitle', ''));
//获取关键词
$data['keywords'] = htmlspecialchars(req_param('keywords', ''));
//获取描述
$data['description'] = htmlspecialchars(req_param('description', ''));
//获取自定义header
$data['custom_header'] = req_param('custom_header', '');
//获取自定义footer
$data['custom_footer'] = req_param('custom_footer', '');
//获取链接模式
$data['link_model'] = req_param('link_model', '');
// 获取链接数量
$data['link_num'] = req_param('link_num', '');
//序列化存储
$value = serialize($data);
if( !empty($data['custom_footer']) ) {
if( !$api->is_subscribe() ) {
$api->err_msg(-2000,'保存失败,自定义footer需要订阅用户才能使用,若未订阅请留空!');
}
}
$api->set_option('s_site',$value);
}
//阻止非订阅用户保存设置
function _deny_set($content,$err_msg) {
global $api;
//验证订阅,返回TRUE或FALSE
if ( !isset($_SESSION['subscribe']) ) {
//验证订阅,返回TRUE或FALSE
$result = $api->is_subscribe();
}
//如果内容是空的,直接允许
if ( empty($content) ) {
return TRUE;
}
else{
if( $_SESSION['subscribe'] === TRUE ) {
return TRUE;
}
else{
$api->err_msg(-2000,$err_msg);
}
}
}
//设置订阅信息
function set_subscribe($api) {
//获取订单ID
$data['order_id'] = htmlspecialchars( trim(req_param('order_id', '')) );
//获取邮箱
$data['email'] = htmlspecialchars( trim(req_param('email', '')) );
//到期时间
$data['end_time'] = htmlspecialchars( trim(req_param('end_time', '')) );
//重置订阅状态
session_start();
$_SESSION['subscribe'] = NULL;
//序列化存储
$value = serialize($data);
//序列化存储到数据库
$api->set_option('s_subscribe',$value);
}
//检查订阅信息
function check_subscribe($api) {
$api->check_subscribe();
}
//检查更新程序
function up_updater($api) {
$api->up_updater();
}
//验证当前版本是否匹配
function check_version($api) {
$version = req_param('version', '');
$api->check_version($version);
}
//设置过渡页面
function set_transition_page($api) {
//获取传递过来的参数
//获取开关
$data['control'] = htmlspecialchars(trim(req_param('control', '')));
//获取游客停留时间
$data['visitor_stay_time'] = intval(req_param('visitor_stay_time', ''));
//获取管理员停留时间
$data['admin_stay_time'] = intval(req_param('admin_stay_time', ''));
//获取菜单
$data['menu'] = req_param('menu', '');
//获取footer
$data['footer'] = req_param('footer', '');
//获取广告
$data['a_d_1'] = req_param('a_d_1', '');
$data['a_d_2'] = req_param('a_d_2', '');
//验证订阅
_deny_set($data['menu'],'保存失败,过渡页菜单需要订阅用户才能使用!');
_deny_set($data['footer'],'保存失败,自定义footer需要订阅用户才能使用!');
_deny_set($data['a_d_1'],'保存失败,自定义广告需要订阅用户才能使用!');
_deny_set($data['a_d_2'],'保存失败,自定义广告需要订阅用户才能使用!');
//序列化存储
$value = serialize($data);
$api->set_option('s_transition_page',$value);
}
//生成create_sk
function create_sk($api) {
$api->create_sk();
}
//获取onenav最新版本号
function get_latest_version() {
try {
$curl = curl_init("https://git.xiaoz.org/xiaoz/onenav/raw/branch/main/version.txt");
curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50");
curl_setopt($curl, CURLOPT_FAILONERROR, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
#设置超时时间,最小为1s(可选)
curl_setopt($curl , CURLOPT_TIMEOUT, 5);
$html = curl_exec($curl);
curl_close($curl);
$data = [
"code" => 200,
"msg" => "",
"data" => $html
];
} catch (\Throwable $th) {
$data = [
"code" => 200,
"msg" => "",
"data" => ""
];
}
exit(json_encode($data));
}
//批量修改链接分类
function batch_modify_category($api) {
//获取id列表
$id = req_param('id', '');
//获取分类ID
$fid = intval(req_param('fid', ''));
$data = [
'id' => $id,
'fid' => $fid
];
$api->batch_modify_category($data);
}
//保存主题参数设置
function save_theme_config($api) {
//获取所有POST数组,并组合为对象
$post_data = $_POST;
//数组转对象
foreach ($post_data as $key => $value) {
$data['config']->$key = $value;
}
$data['name'] = $post_data['name'];
unset($data['config']->name);
$api->save_theme_config($data);
}
//获取主题配置信息
function get_theme_config($api) {
$api->get_theme_config();
}
//导出链接数据
function export_link($api) {
header('Content-Type: text/html; charset=UTF-8');
// 若已有树结构(之前的 export_link 方法),复用;否则自行构建。
$tree = [];
if (method_exists($api,'export_link')) {
$tree = $api->export_link();
}
// 兜底:没有返回结构则直接结束
if (!is_array($tree)) { $tree = []; }
$now = time();
// 递归输出函数(模仿 Chrome 导出格式,目录在前,链接在后)
$printCategory = function ($cat, $depth = 2) use (&$printCategory, $now) {
$indent = str_repeat(' ', $depth);
$indentInner = str_repeat(' ', $depth + 1);
$name = $cat['name'];
// 特殊处理默认分类名称
if ($name === '默认分类') {
$name = 'OneNav默认分类';
}
$name = htmlspecialchars($name, ENT_QUOTES);
$add_date = isset($cat['add_time']) && intval($cat['add_time'])>0 ? intval($cat['add_time']) : $now;
$last_mod = $add_date;
echo "{$indent}<DT><H3 ADD_DATE=\"{$add_date}\" LAST_MODIFIED=\"{$last_mod}\">{$name}</H3>\n";
echo "{$indent}<DL><p>\n";
// 子目录
if (!empty($cat['children'])) {
foreach ($cat['children'] as $sub) {
$printCategory($sub, $depth + 1);
}
}
// 链接
if (!empty($cat['links'])) {
foreach ($cat['links'] as $link) {
$title = htmlspecialchars($link['title'], ENT_QUOTES);
$url = htmlspecialchars($link['url'], ENT_QUOTES);
$l_add = isset($link['add_time']) && intval($link['add_time'])>0 ? intval($link['add_time']) : $now;
echo "{$indentInner}<DT><A HREF=\"{$url}\" ADD_DATE=\"{$l_add}\">{$title}</A>\n";
}
}
echo "{$indent}</DL><p>\n";
};
// 输出头部(保持与 Chrome/Edge 格式一致)
echo <<<HTML
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
HTML;
// 构造浏览器兼容的根目录,名称使用 Bookmarks 以兼容导入器忽略系统根目录的逻辑
$rootAdd = $now;
$rootMod = $now;
echo " <DT><H3 ADD_DATE=\"{$rootAdd}\" LAST_MODIFIED=\"{$rootMod}\" PERSONAL_TOOLBAR_FOLDER=\"true\">Bookmarks</H3>\n";
echo " <DL><p>\n";
foreach ($tree as $topCat) {
$printCategory($topCat, 2);
}
echo " </DL><p>\n";
echo "</DL><p>\n";
}
//导出JSON链接数据
function export_json($api) {
header('Content-Type: application/json; charset=UTF-8');
$payload = [];
if (method_exists($api,'export_json')) {
$payload = $api->export_json();
}
if (!is_array($payload)) { $payload = []; }
$fileName = 'OneNav_Export_' . date('Ymd') . '.json';
header('Content-Disposition: attachment; filename="' . $fileName . '"');
echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n";
}
// 批量设置链接私有属性
function set_link_attribute($api) {
$ids = req_param('ids', []);
$property = intval( req_param('property', '') );
$data = [
"ids" => $ids,
"property" => $property
];
$api->set_link_attribute($data);
}
//获取用户登录状态
function check_login($api) {
$token = trim(req_param('token'));
$api->check_login($token);
}
//删除主题
function delete_theme($api) {
$name = req_param('name', '');
$api->delete_theme($name);
}
//下载主题
function down_theme() {
global $api;
$data['name'] = trim(req_param('name', ''));
$data['key'] = trim( req_param('key', '') );
$data['value'] = trim( req_param('value', '') );
$data['type'] = trim( req_param('type', '') );
$api->down_theme($data);
}
//备份数据库
function backup_db() {
global $api;
$api->backup_db();
}
//数据库备份列表
function backup_db_list() {
global $api;
$api->backup_db_list();
}
//删除单个数据库备份
function del_backup_db() {
global $api;
$name = req_param('name', '');
$api->del_backup_db($name);
}
//回滚数据库
function restore_db() {
global $api;
$name = req_param('name', '');
$api->restore_db($name);
}
//获取APPINFO
function app_info() {
$token = req_token();
global $api;
$api->app_info($token);
}
//下载数据库
function down_db() {
global $api;
$name = req_param('name', '');
$api->down_db($name);
}
//创建分享
function create_share() {
global $api;
$data['add_time'] = date("Y-m-d H:i:s",time());
$data['expire_time'] = req_param('expire_time', '');
$data['password'] = trim(req_param('password', ''));
$data['cid'] = intval(req_param('cid', ''));
$data['note'] = req_param('note', '');
$api->create_share($data);
}
//分享列表
function share_list() {
global $api;
$page = empty(intval(req_param('page', ''))) ? 1 : intval(req_param('page', ''));
$limit = empty(intval(req_param('limit', ''))) ? 10 : intval(req_param('limit', ''));
$data = [
'page' => $page,
'limit' => $limit
];
$api->share_list($data);
}
//获取SID下的链接
function get_sid_links() {
global $api;
$data['sid'] = trim(req_param('sid', ''));
$data['password'] = trim(req_param('password', ''));
$api->get_sid_links($data);
}
//删除分享
function del_share() {
global $api;
$data['id'] = intval(req_param('id', ''));
//var_dump($data['id']);
$api->del_share($data);
}
//站点信息
function site_info() {
global $api;
$api->site_info();
}
//删除图标
function del_link_icon() {
global $api;
$api->del_link_icon();
}
// 修改单行链接
function edit_link_row(){
global $api;
$api->edit_link_row();
}
// 批量修改分类属性
function set_cat_batch() {
$data['ids'] = req_param('ids', []);
$data['property'] = req_param('property', '');
global $api;
$api->set_cat_batch($data);
}
// 全局搜索
function global_search() {
global $api;
$api->global_search();
}
// 上传数据备份文件
function upload_backup(){
global $api;
$api->general_upload('data/backup/',['db3']);
}
// 批量更新排序
function update_link_order(){
global $api;
$api->update_link_order();
}
// 获取订阅信息
function get_subscribe(){
global $api;
$api->get_subscribe();
}
// 获取订阅状态
function get_subscribe_status(){
global $api;
$api->get_subscribe_status();
}