mirror of
https://gitee.com/apiadmin/ApiAdmin.git
synced 2025-04-06 03:58:00 +08:00
Merge branch 'apiadmin-v2.0' of git.oschina.net:xiaoxunzhao/ApiAdmin into apiadmin-v2.0
This commit is contained in:
commit
df73b37f00
Application
Admin
Controller
ApiManageController.class.phpFieldsManageController.class.phpIndexController.class.phpLoginController.class.phpVerificationController.class.php
View
Data
Home
Public/js
README.md@ -30,9 +30,9 @@ class ApiManageController extends BaseController {
|
|||||||
$data = I('post.');
|
$data = I('post.');
|
||||||
$res = D('ApiList')->where(array('id' => $data['id']))->save($data);
|
$res = D('ApiList')->where(array('id' => $data['id']))->save($data);
|
||||||
if( $res === false ) {
|
if( $res === false ) {
|
||||||
S('ApiInfo_' . $data['hash'], 0);
|
|
||||||
$this->ajaxError('操作失败');
|
$this->ajaxError('操作失败');
|
||||||
} else {
|
} else {
|
||||||
|
S('ApiInfo_' . $data['hash'], null);
|
||||||
$this->ajaxSuccess('添加成功');
|
$this->ajaxSuccess('添加成功');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ class ApiManageController extends BaseController {
|
|||||||
$id = I('post.id');
|
$id = I('post.id');
|
||||||
if( $id ) {
|
if( $id ) {
|
||||||
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
||||||
S('ApiInfo_' . $hash, 0);
|
S('ApiInfo_' . $hash, null);
|
||||||
D('ApiList')->open(array('id' => $id));
|
D('ApiList')->open(array('id' => $id));
|
||||||
$this->ajaxSuccess('操作成功');
|
$this->ajaxSuccess('操作成功');
|
||||||
} else {
|
} else {
|
||||||
@ -73,7 +73,7 @@ class ApiManageController extends BaseController {
|
|||||||
$id = I('post.id');
|
$id = I('post.id');
|
||||||
if( $id ) {
|
if( $id ) {
|
||||||
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
||||||
S('ApiInfo_' . $hash, 0);
|
S('ApiInfo_' . $hash, null);
|
||||||
D('ApiList')->close(array('id' => $id));
|
D('ApiList')->close(array('id' => $id));
|
||||||
$this->ajaxSuccess('操作成功');
|
$this->ajaxSuccess('操作成功');
|
||||||
} else {
|
} else {
|
||||||
@ -87,8 +87,11 @@ class ApiManageController extends BaseController {
|
|||||||
$id = I('post.id');
|
$id = I('post.id');
|
||||||
if( $id ) {
|
if( $id ) {
|
||||||
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
$hash = D('ApiList')->where(array('id' => $id))->getField('hash');
|
||||||
S('ApiInfo_' . $hash, 0);
|
S('ApiInfo_' . $hash, null);
|
||||||
D('ApiList')->del(array('id' => $id));
|
D('ApiList')->del(array('id' => $id));
|
||||||
|
S('ApiRequest_' . $hash, null);
|
||||||
|
S('ApiResponse_' . $hash, null);
|
||||||
|
D('ApiFields')->where(array('hash' => $hash))->delete();
|
||||||
$this->ajaxSuccess('操作成功');
|
$this->ajaxSuccess('操作成功');
|
||||||
} else {
|
} else {
|
||||||
$this->ajaxError('缺少参数');
|
$this->ajaxError('缺少参数');
|
||||||
|
@ -61,6 +61,8 @@ class FieldsManageController extends BaseController {
|
|||||||
if ($res === false) {
|
if ($res === false) {
|
||||||
$this->ajaxError('操作失败');
|
$this->ajaxError('操作失败');
|
||||||
} else {
|
} else {
|
||||||
|
S('ApiRequest_' . $data['hash'], null);
|
||||||
|
S('ApiResponse_' . $data['hash'], null);
|
||||||
$this->ajaxSuccess('添加成功');
|
$this->ajaxSuccess('添加成功');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -78,9 +80,9 @@ class FieldsManageController extends BaseController {
|
|||||||
$this->ajaxError('操作失败');
|
$this->ajaxError('操作失败');
|
||||||
} else {
|
} else {
|
||||||
if ($data['type'] == 0) {
|
if ($data['type'] == 0) {
|
||||||
S('ApiRequest_' . $data['hash'], 0);
|
S('ApiRequest_' . $data['hash'], null);
|
||||||
} else {
|
} else {
|
||||||
S('ApiResponse_' . $data['hash'], 0);
|
S('ApiResponse_' . $data['hash'], null);
|
||||||
}
|
}
|
||||||
$this->ajaxSuccess('添加成功');
|
$this->ajaxSuccess('添加成功');
|
||||||
}
|
}
|
||||||
@ -101,9 +103,9 @@ class FieldsManageController extends BaseController {
|
|||||||
if ($id) {
|
if ($id) {
|
||||||
$detail = D('ApiFields')->where(array('id' => $id))->find();
|
$detail = D('ApiFields')->where(array('id' => $id))->find();
|
||||||
if ($detail['type'] == 0) {
|
if ($detail['type'] == 0) {
|
||||||
S('ApiRequest_' . $detail['hash'], 0);
|
S('ApiRequest_' . $detail['hash'], null);
|
||||||
} else {
|
} else {
|
||||||
S('ApiResponse_' . $detail['hash'], 0);
|
S('ApiResponse_' . $detail['hash'], null);
|
||||||
}
|
}
|
||||||
D('ApiFields')->where(array('id' => $id))->delete();
|
D('ApiFields')->where(array('id' => $id))->delete();
|
||||||
$this->ajaxSuccess('操作成功');
|
$this->ajaxSuccess('操作成功');
|
||||||
@ -143,11 +145,11 @@ class FieldsManageController extends BaseController {
|
|||||||
D('ApiFields')->addAll($addData);
|
D('ApiFields')->addAll($addData);
|
||||||
}
|
}
|
||||||
if ($type == 0) {
|
if ($type == 0) {
|
||||||
S('ApiRequest_' . $hash, 0);
|
S('ApiRequest_' . $hash, null);
|
||||||
} else {
|
} else {
|
||||||
S('ApiResponse_' . $hash, 0);
|
S('ApiResponse_' . $hash, null);
|
||||||
}
|
}
|
||||||
S('ApiReturnType_' . $hash, 0);
|
S('ApiReturnType_' . $hash, null);
|
||||||
$this->ajaxSuccess('操作成功');
|
$this->ajaxSuccess('操作成功');
|
||||||
} else {
|
} else {
|
||||||
$this->display();
|
$this->display();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Admin\Controller;
|
namespace Admin\Controller;
|
||||||
|
|
||||||
|
|
||||||
@ -9,24 +10,28 @@ class IndexController extends BaseController {
|
|||||||
$isAdmin = isAdministrator();
|
$isAdmin = isAdministrator();
|
||||||
$list = array();
|
$list = array();
|
||||||
$menuAll = $this->allMenu;
|
$menuAll = $this->allMenu;
|
||||||
foreach ($menuAll as $key => $menu) {
|
|
||||||
if($menu['hide'] != 0){
|
|
||||||
unset($menuAll[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($menuAll as $menu) {
|
foreach ($menuAll as $menu) {
|
||||||
if($isAdmin){
|
if ($menu['hide'] == 0) {
|
||||||
$menu['url'] = U($menu['url']);
|
if ($isAdmin) {
|
||||||
$list[] = $menu;
|
|
||||||
}else{
|
|
||||||
$authObj = new Auth();
|
|
||||||
$authList = $authObj->getAuthList($this->uid);
|
|
||||||
if (in_array(strtolower($menu['url']), $authList) || $menu['url'] == '') {
|
|
||||||
$menu['url'] = U($menu['url']);
|
$menu['url'] = U($menu['url']);
|
||||||
$list[] = $menu;
|
$list[] = $menu;
|
||||||
|
} else {
|
||||||
|
$authObj = new Auth();
|
||||||
|
$authList = $authObj->getAuthList($this->uid);
|
||||||
|
if (in_array(strtolower($menu['url']), $authList) || $menu['url'] == '') {
|
||||||
|
$menu['url'] = U($menu['url']);
|
||||||
|
$list[] = $menu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$list = listToTree($list);
|
||||||
|
foreach ($list as $key => $item) {
|
||||||
|
if(empty($item['_child']) && $item['url'] != U('Index/welcome')){
|
||||||
|
unset($list[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$list = formatTree($list);
|
||||||
$this->assign('list', $list);
|
$this->assign('list', $list);
|
||||||
$this->display();
|
$this->display();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,13 @@ class LoginController extends BaseController {
|
|||||||
public function login() {
|
public function login() {
|
||||||
$pass = user_md5(I('post.password'));
|
$pass = user_md5(I('post.password'));
|
||||||
$user = I('post.username');
|
$user = I('post.username');
|
||||||
|
|
||||||
|
$challenge = I('post.geetest_challenge');
|
||||||
|
$validate = I('post.geetest_validate');
|
||||||
|
if(!$challenge || md5($challenge) != $validate){
|
||||||
|
$this->ajaxError('请先通过验证!');
|
||||||
|
}
|
||||||
|
|
||||||
$userInfo = D('ApiUser')->where(array('username' => $user, 'password' => $pass))->find();
|
$userInfo = D('ApiUser')->where(array('username' => $user, 'password' => $pass))->find();
|
||||||
if (!empty($userInfo)) {
|
if (!empty($userInfo)) {
|
||||||
if ($userInfo['status']) {
|
if ($userInfo['status']) {
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 工具类控制器,不受权限等控制
|
||||||
|
* @since 2017/06/23 创建
|
||||||
|
* @author zhaoxiang <zhaoxiang051405@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Admin\Controller;
|
||||||
|
|
||||||
|
|
||||||
|
use Think\Controller;
|
||||||
|
|
||||||
|
class VerificationController extends Controller {
|
||||||
|
|
||||||
|
private $gt_captcha_id = 'YourID';
|
||||||
|
private $gt_private_key = 'YourKey';
|
||||||
|
|
||||||
|
public function gt(){
|
||||||
|
$rnd1 = md5(rand(0, 100));
|
||||||
|
$rnd2 = md5(rand(0, 100));
|
||||||
|
$challenge = $rnd1 . substr($rnd2, 0, 2);
|
||||||
|
$result = array(
|
||||||
|
'success' => 0,
|
||||||
|
'gt' => $this->gt_captcha_id,
|
||||||
|
'challenge' => $challenge,
|
||||||
|
'new_captcha'=>1
|
||||||
|
);
|
||||||
|
$this->ajaxReturn($result);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -52,6 +51,33 @@
|
|||||||
.login-box .version{
|
.login-box .version{
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
.inp {
|
||||||
|
border: 1px solid gray;
|
||||||
|
padding: 0 10px;
|
||||||
|
width: 200px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
border: 1px solid gray;
|
||||||
|
width: 100px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#embed-captcha {
|
||||||
|
width: 300px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#notice {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="login-body">
|
<body class="login-body">
|
||||||
@ -73,6 +99,13 @@
|
|||||||
<input type="password" name="password" required class="layui-input" lay-verify="password" placeholder="请输入密码"/>
|
<input type="password" name="password" required class="layui-input" lay-verify="password" placeholder="请输入密码"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-inline">
|
||||||
|
<div id="embed-captcha"></div>
|
||||||
|
<p id="wait" class="show">正在加载验证码......</p>
|
||||||
|
<p id="notice" class="hide">请先完成验证</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<button type="reset" class="layui-btn btn-reset layui-btn-danger" >重置</button>
|
<button type="reset" class="layui-btn btn-reset layui-btn-danger" >重置</button>
|
||||||
<button type="button" class="layui-btn btn-submit" lay-submit="" lay-filter="sub">立即登录</button>
|
<button type="button" class="layui-btn btn-submit" lay-submit="" lay-filter="sub">立即登录</button>
|
||||||
@ -80,6 +113,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="__PUBLIC__/layui/layui.js"></script>
|
<script type="text/javascript" src="__PUBLIC__/layui/layui.js"></script>
|
||||||
|
<script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
|
||||||
|
<script src="__PUBLIC__/js/gt.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
layui.use(['form', 'layer'], function () {
|
layui.use(['form', 'layer'], function () {
|
||||||
var $ = layui.jquery,form = layui.form(),layer = layui.layer;
|
var $ = layui.jquery,form = layui.form(),layer = layui.layer;
|
||||||
@ -114,5 +149,38 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var handlerEmbed = function (captchaObj) {
|
||||||
|
$("#embed-submit").click(function (e) {
|
||||||
|
var validate = captchaObj.getValidate();
|
||||||
|
if (!validate) {
|
||||||
|
$("#notice")[0].className = "show";
|
||||||
|
setTimeout(function () {
|
||||||
|
$("#notice")[0].className = "hide";
|
||||||
|
}, 2000);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
captchaObj.appendTo("#embed-captcha");
|
||||||
|
captchaObj.onReady(function () {
|
||||||
|
$("#wait")[0].className = "hide";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$.ajax({
|
||||||
|
url: "{:U('Verification/gt')}?t=" + (new Date()).getTime(),
|
||||||
|
type: "get",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
console.log(data);
|
||||||
|
initGeetest({
|
||||||
|
gt: data.gt,
|
||||||
|
challenge: data.challenge,
|
||||||
|
new_captcha: data.new_captcha,
|
||||||
|
product: "float",
|
||||||
|
offline: !data.success
|
||||||
|
}, handlerEmbed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -7,21 +7,21 @@
|
|||||||
<input type="hidden" name="groupId" value="{:I('get.group_id')}">
|
<input type="hidden" name="groupId" value="{:I('get.group_id')}">
|
||||||
<volist name="list" id="vo">
|
<volist name="list" id="vo">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<input lay-skin="primary" type="checkbox" lay-filter="admin-check" name="rule[{$vo['id']}]" value="{$vo['url']}" title="{$vo['name']}" {:(in_array($vo['url'], $hasRule)?'checked':'')}>
|
<input lay-skin="primary" type="checkbox" data-id="{$vo['id']}" lay-filter="admin-check" name="rule[{$vo['id']}]" value="{$vo['url']}" title="{$vo['name']}" {:(in_array($vo['url'], $hasRule)?'checked':'')}>
|
||||||
</div>
|
</div>
|
||||||
<if condition="count($vo['_child'])">
|
<if condition="count($vo['_child'])">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<div style="margin-left: 50px;">
|
<div style="margin-left: 50px;">
|
||||||
<volist name="vo['_child']" id="child">
|
<volist name="vo['_child']" id="child">
|
||||||
<input lay-skin="primary" type="checkbox" lay-filter="admin-check" fid="{$vo['url']}" name="rule[{$child['id']}]" value="{$child['url']}" title="{$child['name']}" {:(in_array($child['url'], $hasRule)?'checked':'')}>
|
<input lay-skin="primary" type="checkbox" lay-filter="admin-check" data-id="{$child['id']}" fid="{$vo['id']}" name="rule[{$child['id']}]" value="{$child['url']}" title="{$child['name']}" {:(in_array($child['url'], $hasRule)?'checked':'')}>
|
||||||
<if condition="count($child['_child'])">
|
<if condition="count($child['_child'])">
|
||||||
<div style="margin-left: 50px;">
|
<div style="margin-left: 50px;">
|
||||||
<volist name="child['_child']" id="_child">
|
<volist name="child['_child']" id="_child">
|
||||||
<input lay-skin="primary" type="checkbox" pid="{$vo['url']}" fid="{$child['url']}" name="rule[{$_child['id']}]" value="{$_child['url']}" title="{$_child['name']}" {:(in_array($_child['url'], $hasRule)?'checked':'')}>
|
<input lay-skin="primary" type="checkbox" pid="{$vo['id']}" data-id="{$_child['id']}" fid="{$child['id']}" name="rule[{$_child['id']}]" value="{$_child['url']}" title="{$_child['name']}" {:(in_array($_child['url'], $hasRule)?'checked':'')}>
|
||||||
</volist>
|
</volist>
|
||||||
</div>
|
</div>
|
||||||
</if>
|
</if>
|
||||||
</volist>
|
</volist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</if>
|
</if>
|
||||||
@ -39,13 +39,14 @@
|
|||||||
layui.use('form', function(){
|
layui.use('form', function(){
|
||||||
var form = layui.form();
|
var form = layui.form();
|
||||||
form.on('checkbox(admin-check)', function(data){
|
form.on('checkbox(admin-check)', function(data){
|
||||||
|
var dataId = $(this).attr('data-id');
|
||||||
var $el = data.elem;
|
var $el = data.elem;
|
||||||
if( $el.checked ){
|
if( $el.checked ){
|
||||||
$('input[fid="'+$el.value+'"]').prop('checked','checked');
|
$('input[fid="'+dataId+'"]').prop('checked','checked');
|
||||||
$('input[pid="'+$el.value+'"]').prop('checked','checked');
|
$('input[pid="'+dataId+'"]').prop('checked','checked');
|
||||||
}else{
|
}else{
|
||||||
$('input[fid="'+$el.value+'"]').prop('checked', false);
|
$('input[fid="'+dataId+'"]').prop('checked', false);
|
||||||
$('input[pid="'+$el.value+'"]').prop('checked', false);
|
$('input[pid="'+dataId+'"]').prop('checked', false);
|
||||||
}
|
}
|
||||||
form.render();
|
form.render();
|
||||||
});
|
});
|
||||||
|
@ -298,6 +298,25 @@ CREATE TABLE `api_user_data` (
|
|||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理员数据表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理员数据表';
|
||||||
|
|
||||||
|
|
||||||
|
# Dump of table api_document
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `api_document`;
|
||||||
|
|
||||||
|
CREATE TABLE `api_document` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`key` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '授权秘钥',
|
||||||
|
`endTime` INT(11) NOT NULL DEFAULT '0' COMMENT '失效时间戳',
|
||||||
|
`times` INT(11) NOT NULL DEFAULT '0' COMMENT '访问次数',
|
||||||
|
`lastTime` INT(11) NOT NULL DEFAULT '0' COMMENT '最后访问时间',
|
||||||
|
`lastIp` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '最后访问IP',
|
||||||
|
`createTime` INT(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
|
||||||
|
`keep` INT(11) NOT NULL DEFAULT '0' COMMENT '有效时长(单位小时)',
|
||||||
|
`status` TINYINT(1) NOT NULL DEFAULT '1' COMMENT '1生效,0失效',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE INDEX `key` (`key`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文档访问秘钥';
|
||||||
|
|
||||||
|
|
||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
@ -67,15 +67,6 @@ class ApiController extends BaseController {
|
|||||||
$this->header = array_change_key_case($this->header, CASE_UPPER);
|
$this->header = array_change_key_case($this->header, CASE_UPPER);
|
||||||
ApiLog::setHeader($this->header);
|
ApiLog::setHeader($this->header);
|
||||||
|
|
||||||
if ($this->apiDetail['accessToken'] && !$this->apiDetail['isTest']) {
|
|
||||||
$this->checkAccessToken();
|
|
||||||
}
|
|
||||||
if (!$this->apiDetail['isTest']) {
|
|
||||||
$this->checkVersion();
|
|
||||||
}
|
|
||||||
$this->checkLogin();
|
|
||||||
|
|
||||||
unset($getArr['hash']);
|
|
||||||
switch ($this->apiDetail['method']) {
|
switch ($this->apiDetail['method']) {
|
||||||
case 0:
|
case 0:
|
||||||
$this->param = array_merge($getArr, $postArr);
|
$this->param = array_merge($getArr, $postArr);
|
||||||
@ -93,6 +84,15 @@ class ApiController extends BaseController {
|
|||||||
$this->param = $data;
|
$this->param = $data;
|
||||||
}
|
}
|
||||||
ApiLog::setRequest($this->param);
|
ApiLog::setRequest($this->param);
|
||||||
|
|
||||||
|
if ($this->apiDetail['accessToken'] && !$this->apiDetail['isTest']) {
|
||||||
|
$this->checkAccessToken();
|
||||||
|
}
|
||||||
|
if (!$this->apiDetail['isTest']) {
|
||||||
|
$this->checkVersion();
|
||||||
|
}
|
||||||
|
$this->checkLogin();
|
||||||
|
unset($getArr['hash']);
|
||||||
$this->iniApi();
|
$this->iniApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class ApiController extends BaseController {
|
|||||||
if (!is_array($userInfo) || !isset($userInfo['passport_uid'])) {
|
if (!is_array($userInfo) || !isset($userInfo['passport_uid'])) {
|
||||||
Response::error(ReturnCode::AUTH_ERROR, 'user-token不匹配');
|
Response::error(ReturnCode::AUTH_ERROR, 'user-token不匹配');
|
||||||
}
|
}
|
||||||
C('USER_INFO', $userInfo);
|
ApiLog::setUserInfo($userInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,87 +7,91 @@
|
|||||||
namespace Home\ORG;
|
namespace Home\ORG;
|
||||||
|
|
||||||
|
|
||||||
class ApiLog{
|
class ApiLog {
|
||||||
|
|
||||||
private static $appInfo = 'null';
|
private static $appInfo = 'null';
|
||||||
private static $apiInfo = 'null';
|
private static $apiInfo = 'null';
|
||||||
private static $request = 'null';
|
private static $request = 'null';
|
||||||
private static $requestAfterFilter = 'null';
|
private static $requestAfterFilter = 'null';
|
||||||
private static $response = 'null';
|
private static $response = 'null';
|
||||||
private static $header = 'null';
|
private static $header = 'null';
|
||||||
private static $userInfo = 'null';
|
private static $userInfo = 'null';
|
||||||
private static $separator = '###';
|
private static $separator = '###';
|
||||||
|
|
||||||
public static function setAppInfo($data){
|
public static function setAppInfo($data) {
|
||||||
self::$appInfo = $data['app_id'] . "({$data['app_name']}) {$data['device_id']}";
|
self::$appInfo = $data['app_id'] . self::$separator . $data['app_name'] . self::$separator . $data['device_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setHeader($data){
|
public static function setHeader($data) {
|
||||||
$userToken = (isset($data['USER-TOKEN']) && !empty($data['USER-TOKEN'])) ? $data['USER-TOKEN'] : 'null';
|
$userToken = (isset($data['USER-TOKEN']) && !empty($data['USER-TOKEN'])) ? $data['USER-TOKEN'] : 'null';
|
||||||
$accessToken = (isset($data['ACCESS-TOKEN']) && !empty($data['ACCESS-TOKEN'])) ? $data['ACCESS-TOKEN'] : 'null';
|
$accessToken = (isset($data['ACCESS-TOKEN']) && !empty($data['ACCESS-TOKEN'])) ? $data['ACCESS-TOKEN'] : 'null';
|
||||||
self::$header = $accessToken . self::$separator . $userToken . self::$separator . $data['VERSION'] . self::$separator . $cas;
|
$cas = (isset($data['CAS']) && !empty($data['CAS'])) ? $data['CAS'] : 'null';
|
||||||
}
|
self::$header = $accessToken . self::$separator . $userToken . self::$separator . $data['VERSION'] . self::$separator . $cas;
|
||||||
|
}
|
||||||
|
|
||||||
public static function setApiInfo($data){
|
public static function setApiInfo($data) {
|
||||||
self::$apiInfo = $data['apiName'] . self::$separator . $data['hash'];
|
self::$apiInfo = $data['apiName'] . self::$separator . $data['hash'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setUserInfo($data){
|
public static function setUserInfo($data) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$data = json_encode($data);
|
$data = json_encode($data);
|
||||||
}
|
}
|
||||||
self::$userInfo = $data;
|
self::$userInfo = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setRequest($data){
|
public static function setRequest($data) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$data = json_encode($data);
|
$data = json_encode($data);
|
||||||
}
|
}
|
||||||
self::$request = $data;
|
self::$request = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setRequestAfterFilter($data){
|
public static function setRequestAfterFilter($data) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$data = json_encode($data);
|
$data = json_encode($data);
|
||||||
}
|
}
|
||||||
self::$requestAfterFilter = $data;
|
self::$requestAfterFilter = $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setResponse($data){
|
public static function setResponse($data, $code) {
|
||||||
if (is_array($data)) {
|
if (is_array($data)) {
|
||||||
$data = json_encode($data);
|
$data = json_encode($data);
|
||||||
}
|
}
|
||||||
self::$response = $data;
|
self::$response = $code . self::$separator . $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function save(){
|
public static function save() {
|
||||||
$logPath = APP_PATH . '/ApiLog/' . date('YmdH') . '.log';
|
$logPath = APP_PATH . '/ApiLog/' . date('YmdH') . '.log';
|
||||||
$logStr = implode(self::$separator, array(
|
if (self::$appInfo == 'null') {
|
||||||
self::$apiInfo,
|
self::$appInfo = 'null' . self::$separator . 'null' . self::$separator . 'null';
|
||||||
date('H:i:s'),
|
}
|
||||||
self::$request,
|
$logStr = implode(self::$separator, array(
|
||||||
self::$header,
|
self::$apiInfo,
|
||||||
self::$response,
|
date('Y-m-d H:i:s'),
|
||||||
self::$requestAfterFilter,
|
self::$request,
|
||||||
self::$appInfo,
|
self::$header,
|
||||||
self::$userInfo
|
self::$response,
|
||||||
));
|
self::$requestAfterFilter,
|
||||||
|
self::$appInfo,
|
||||||
|
self::$userInfo
|
||||||
|
));
|
||||||
|
|
||||||
@file_put_contents($logPath, $logStr . "\n", FILE_APPEND);
|
@file_put_contents($logPath, $logStr . "\n", FILE_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $log 被记录的内容
|
* @param string $log 被记录的内容
|
||||||
* @param string $type 日志文件名称
|
* @param string $type 日志文件名称
|
||||||
* @param string $filePath
|
* @param string $filePath
|
||||||
*/
|
*/
|
||||||
public static function writeLog($log, $type = 'sql', $filePath = './Application/Runtime/'){
|
public static function writeLog($log, $type = 'sql', $filePath = './Application/Runtime/') {
|
||||||
$filename = $filePath . date("Ymd") . '_' . $type . ".log";
|
$filename = $filePath . date("Ymd") . '_' . $type . ".log";
|
||||||
@$handle = fopen($filename, "a+");
|
@$handle = fopen($filename, "a+");
|
||||||
@fwrite($handle, date('Y-m-d H:i:s') . "\t" . $log . "\r\n");
|
@fwrite($handle, date('Y-m-d H:i:s') . "\t" . $log . "\r\n");
|
||||||
@fclose($handle);
|
@fclose($handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -13,7 +13,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<div class="ui text container" style="max-width: none !important;">
|
<div class="ui text container" style="max-width: none !important;">
|
||||||
<div class="ui floating message">
|
<div class="ui floating message">
|
||||||
<h2 class='ui header'>接口唯一标识:<a target="_blank" href="http://{$_SERVER['HTTP_HOST']}/api/{$apiInfo['hash']}">{$apiInfo['hash']}</a>({$apiInfo['apiName']})</h2><br />
|
<h2 class='ui header'>接口唯一标识:<a target="_blank" href="{:U('/api/'.$apiInfo['hash'])}">{$apiInfo['hash']}</a>({$apiInfo['apiName']})</h2><br />
|
||||||
<switch name="apiInfo['method']" >
|
<switch name="apiInfo['method']" >
|
||||||
<case value="1" break="1"> <span class='ui orange tag label'>POST</span></case>
|
<case value="1" break="1"> <span class='ui orange tag label'>POST</span></case>
|
||||||
<case value="2" break="1"> <span class='ui blue tag label'>GET</span></case>
|
<case value="2" break="1"> <span class='ui blue tag label'>GET</span></case>
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
<button class="ui orange button" style="margin-top: 15px">算法详解</button>
|
<button class="ui orange button" style="margin-top: 15px">算法详解</button>
|
||||||
</a>
|
</a>
|
||||||
<div class="ui teal message">
|
<div class="ui teal message">
|
||||||
<strong>API统一访问地址:</strong> http://{$_SERVER['HTTP_HOST']}/api/接口唯一标识
|
<php>$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';</php>
|
||||||
|
<strong>API统一访问地址:</strong> {$http_type}{$_SERVER['HTTP_HOST']}/api/接口唯一标识
|
||||||
</div>
|
</div>
|
||||||
<div class="ui floating message">
|
<div class="ui floating message">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -60,21 +60,20 @@ layui.define(function(exports) {
|
|||||||
/**
|
/**
|
||||||
* 开始循环
|
* 开始循环
|
||||||
*/
|
*/
|
||||||
this.each = function(arr) {
|
this.each = function(arr,index) {
|
||||||
if(arr == undefined) {
|
if(arr == undefined) {
|
||||||
arr = _father;
|
arr = _father;
|
||||||
}
|
}
|
||||||
|
var index = index ? index: 0;
|
||||||
for(var i in arr) {
|
for(var i in arr) {
|
||||||
var children = _that.getChildren(arr[i][_idName]);
|
var children = _that.getChildren(arr[i][_idName]);
|
||||||
var counter = children.length;
|
var counter = children.length;
|
||||||
|
|
||||||
_that.forBefore(arr[i], i, counter);
|
_that.forBefore(arr[i], i, counter);
|
||||||
_that.forCurr(arr[i], i, counter);
|
_that.forCurr(arr[i], i, counter);
|
||||||
|
|
||||||
if(counter) {
|
if(counter && index==0) {
|
||||||
_that.callBefore(arr[i], i);
|
_that.callBefore(arr[i], i);
|
||||||
_that.each(children);
|
_that.each(children,2);
|
||||||
_that.callAfter(arr[i], i);
|
_that.callAfter(arr[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,4 +81,4 @@ layui.define(function(exports) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
277
Public/js/gt.js
Normal file
277
Public/js/gt.js
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
/* initGeetest 1.0.0
|
||||||
|
* 用于加载id对应的验证码库,并支持宕机模式
|
||||||
|
* 暴露 initGeetest 进行验证码的初始化
|
||||||
|
* 一般不需要用户进行修改
|
||||||
|
*/
|
||||||
|
(function (global, factory) {
|
||||||
|
"use strict";
|
||||||
|
if (typeof module === "object" && typeof module.exports === "object") {
|
||||||
|
// CommonJS
|
||||||
|
module.exports = global.document ?
|
||||||
|
factory(global, true) :
|
||||||
|
function (w) {
|
||||||
|
if (!w.document) {
|
||||||
|
throw new Error("Geetest requires a window with a document");
|
||||||
|
}
|
||||||
|
return factory(w);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
factory(global);
|
||||||
|
}
|
||||||
|
})(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
|
||||||
|
"use strict";
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
throw new Error('Geetest requires browser environment');
|
||||||
|
}
|
||||||
|
var document = window.document;
|
||||||
|
var Math = window.Math;
|
||||||
|
var head = document.getElementsByTagName("head")[0];
|
||||||
|
|
||||||
|
function _Object(obj) {
|
||||||
|
this._obj = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Object.prototype = {
|
||||||
|
_each: function (process) {
|
||||||
|
var _obj = this._obj;
|
||||||
|
for (var k in _obj) {
|
||||||
|
if (_obj.hasOwnProperty(k)) {
|
||||||
|
process(k, _obj[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function Config(config) {
|
||||||
|
var self = this;
|
||||||
|
new _Object(config)._each(function (key, value) {
|
||||||
|
self[key] = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.prototype = {
|
||||||
|
api_server: 'api.geetest.com',
|
||||||
|
protocol: 'http://',
|
||||||
|
type_path: '/gettype.php',
|
||||||
|
fallback_config: {
|
||||||
|
slide: {
|
||||||
|
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
|
||||||
|
type: 'slide',
|
||||||
|
slide: '/static/js/geetest.0.0.0.js'
|
||||||
|
},
|
||||||
|
fullpage: {
|
||||||
|
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
|
||||||
|
type: 'fullpage',
|
||||||
|
fullpage: '/static/js/fullpage.0.0.0.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_get_fallback_config: function () {
|
||||||
|
var self = this;
|
||||||
|
if (isString(self.type)) {
|
||||||
|
return self.fallback_config[self.type];
|
||||||
|
} else if (self.new_captcha) {
|
||||||
|
return self.fallback_config.fullpage;
|
||||||
|
} else {
|
||||||
|
return self.fallback_config.slide;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_extend: function (obj) {
|
||||||
|
var self = this;
|
||||||
|
new _Object(obj)._each(function (key, value) {
|
||||||
|
self[key] = value;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var isNumber = function (value) {
|
||||||
|
return (typeof value === 'number');
|
||||||
|
};
|
||||||
|
var isString = function (value) {
|
||||||
|
return (typeof value === 'string');
|
||||||
|
};
|
||||||
|
var isBoolean = function (value) {
|
||||||
|
return (typeof value === 'boolean');
|
||||||
|
};
|
||||||
|
var isObject = function (value) {
|
||||||
|
return (typeof value === 'object' && value !== null);
|
||||||
|
};
|
||||||
|
var isFunction = function (value) {
|
||||||
|
return (typeof value === 'function');
|
||||||
|
};
|
||||||
|
var callbacks = {};
|
||||||
|
var status = {};
|
||||||
|
var random = function () {
|
||||||
|
return parseInt(Math.random() * 10000) + (new Date()).valueOf();
|
||||||
|
};
|
||||||
|
var loadScript = function (url, cb) {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.charset = "UTF-8";
|
||||||
|
script.async = true;
|
||||||
|
script.onerror = function () {
|
||||||
|
cb(true);
|
||||||
|
};
|
||||||
|
var loaded = false;
|
||||||
|
script.onload = script.onreadystatechange = function () {
|
||||||
|
if (!loaded &&
|
||||||
|
(!script.readyState ||
|
||||||
|
"loaded" === script.readyState ||
|
||||||
|
"complete" === script.readyState)) {
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
setTimeout(function () {
|
||||||
|
cb(false);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
script.src = url;
|
||||||
|
head.appendChild(script);
|
||||||
|
};
|
||||||
|
var normalizeDomain = function (domain) {
|
||||||
|
return domain.replace(/^https?:\/\/|\/$/g, '');
|
||||||
|
};
|
||||||
|
var normalizePath = function (path) {
|
||||||
|
path = path.replace(/\/+/g, '/');
|
||||||
|
if (path.indexOf('/') !== 0) {
|
||||||
|
path = '/' + path;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
var normalizeQuery = function (query) {
|
||||||
|
if (!query) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var q = '?';
|
||||||
|
new _Object(query)._each(function (key, value) {
|
||||||
|
if (isString(value) || isNumber(value) || isBoolean(value)) {
|
||||||
|
q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (q === '?') {
|
||||||
|
q = '';
|
||||||
|
}
|
||||||
|
return q.replace(/&$/, '');
|
||||||
|
};
|
||||||
|
var makeURL = function (protocol, domain, path, query) {
|
||||||
|
domain = normalizeDomain(domain);
|
||||||
|
|
||||||
|
var url = normalizePath(path) + normalizeQuery(query);
|
||||||
|
if (domain) {
|
||||||
|
url = protocol + domain + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
var load = function (protocol, domains, path, query, cb) {
|
||||||
|
var tryRequest = function (at) {
|
||||||
|
|
||||||
|
var url = makeURL(protocol, domains[at], path, query);
|
||||||
|
loadScript(url, function (err) {
|
||||||
|
if (err) {
|
||||||
|
if (at >= domains.length - 1) {
|
||||||
|
cb(true);
|
||||||
|
} else {
|
||||||
|
tryRequest(at + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
tryRequest(0);
|
||||||
|
};
|
||||||
|
var jsonp = function (domains, path, config, callback) {
|
||||||
|
if (isObject(config.getLib)) {
|
||||||
|
config._extend(config.getLib);
|
||||||
|
callback(config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (config.offline) {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cb = "geetest_" + random();
|
||||||
|
window[cb] = function (data) {
|
||||||
|
if (data.status === 'success') {
|
||||||
|
callback(data.data);
|
||||||
|
} else if (!data.status) {
|
||||||
|
callback(data);
|
||||||
|
} else {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
}
|
||||||
|
window[cb] = undefined;
|
||||||
|
try {
|
||||||
|
delete window[cb];
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
load(config.protocol, domains, path, {
|
||||||
|
gt: config.gt,
|
||||||
|
callback: cb
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
callback(config._get_fallback_config());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var throwError = function (errorType, config) {
|
||||||
|
var errors = {
|
||||||
|
networkError: '网络错误'
|
||||||
|
};
|
||||||
|
if (typeof config.onError === 'function') {
|
||||||
|
config.onError(errors[errorType]);
|
||||||
|
} else {
|
||||||
|
throw new Error(errors[errorType]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var detect = function () {
|
||||||
|
return !!window.Geetest;
|
||||||
|
};
|
||||||
|
if (detect()) {
|
||||||
|
status.slide = "loaded";
|
||||||
|
}
|
||||||
|
var initGeetest = function (userConfig, callback) {
|
||||||
|
var config = new Config(userConfig);
|
||||||
|
if (userConfig.https) {
|
||||||
|
config.protocol = 'https://';
|
||||||
|
} else if (!userConfig.protocol) {
|
||||||
|
config.protocol = window.location.protocol + '//';
|
||||||
|
}
|
||||||
|
jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) {
|
||||||
|
var type = newConfig.type;
|
||||||
|
var init = function () {
|
||||||
|
config._extend(newConfig);
|
||||||
|
callback(new window.Geetest(config));
|
||||||
|
};
|
||||||
|
callbacks[type] = callbacks[type] || [];
|
||||||
|
var s = status[type] || 'init';
|
||||||
|
if (s === 'init') {
|
||||||
|
status[type] = 'loading';
|
||||||
|
callbacks[type].push(init);
|
||||||
|
load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
|
||||||
|
if (err) {
|
||||||
|
status[type] = 'fail';
|
||||||
|
throwError('networkError', config);
|
||||||
|
} else {
|
||||||
|
status[type] = 'loaded';
|
||||||
|
var cbs = callbacks[type];
|
||||||
|
for (var i = 0, len = cbs.length; i < len; i = i + 1) {
|
||||||
|
var cb = cbs[i];
|
||||||
|
if (isFunction(cb)) {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callbacks[type] = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (s === "loaded") {
|
||||||
|
init();
|
||||||
|
} else if (s === "fail") {
|
||||||
|
throwError('networkError', config);
|
||||||
|
} else if (s === "loading") {
|
||||||
|
callbacks[type].push(init);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.initGeetest = initGeetest;
|
||||||
|
return initGeetest;
|
||||||
|
});
|
||||||
|
|
@ -21,17 +21,21 @@
|
|||||||
|
|
||||||
[http://git.oschina.net/xiaoxunzhao/ApiAdmin/wikis/%E9%A1%B9%E7%9B%AE%E5%AE%89%E8%A3%852.0](http://git.oschina.net/xiaoxunzhao/ApiAdmin/wikis/%E9%A1%B9%E7%9B%AE%E5%AE%89%E8%A3%852.0)
|
[http://git.oschina.net/xiaoxunzhao/ApiAdmin/wikis/%E9%A1%B9%E7%9B%AE%E5%AE%89%E8%A3%852.0](http://git.oschina.net/xiaoxunzhao/ApiAdmin/wikis/%E9%A1%B9%E7%9B%AE%E5%AE%89%E8%A3%852.0)
|
||||||
|
|
||||||
|
**二次开发文档**
|
||||||
|
|
||||||
|
[http://www.w3cschool.cn/apiadmin_v2/](http://www.w3cschool.cn/apiadmin_v2/)
|
||||||
|
|
||||||
**源码地址**
|
**源码地址**
|
||||||
|
|
||||||
国内OSC:[http://git.oschina.net/xiaoxunzhao/ApiAdmin](http://git.oschina.net/xiaoxunzhao/ApiAdmin)
|
国内OSC:[http://git.oschina.net/xiaoxunzhao/ApiAdmin](http://git.oschina.net/xiaoxunzhao/ApiAdmin)
|
||||||
|
|
||||||
国际GitHub(暂未开通):[https://github.com/Zhao-github/ApiAdmin](https://github.com/Zhao-github/ApiAdmin)
|
国际GitHub:[https://github.com/Zhao-github/ApiAdmin](https://github.com/Zhao-github/ApiAdmin)
|
||||||
|
|
||||||
**下载地址**
|
**下载地址**
|
||||||
|
|
||||||
国内OSC:[http://git.oschina.net/xiaoxunzhao/ApiAdmin/releases](http://git.oschina.net/xiaoxunzhao/ApiAdmin)
|
国内OSC:[http://git.oschina.net/xiaoxunzhao/ApiAdmin/releases](http://git.oschina.net/xiaoxunzhao/ApiAdmin)
|
||||||
|
|
||||||
国际GitHub(暂未开通):[https://github.com/Zhao-github/ApiAdmin/releases](https://github.com/Zhao-github/ApiAdmin/releases)
|
国际GitHub:[https://github.com/Zhao-github/ApiAdmin/releases](https://github.com/Zhao-github/ApiAdmin/releases)
|
||||||
|
|
||||||
**项目构成**
|
**项目构成**
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user