commit edc98d40c6e1ccb046c70646862194443cd89a49 Author: shirakun Date: Thu Sep 28 14:11:49 2017 +0800 Signed-off-by: shirakun diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d1ff56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/application/config.php +/application/database.php +/runtime +/.user.ini +/index.html +/404.html diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..574a39c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,32 @@ + +ThinkPHP遵循Apache2开源协议发布,并提供免费使用。 +版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn) +All rights reserved。 +ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。 + +Apache Licence是著名的非盈利开源组织Apache采用的协议。 +该协议和BSD类似,鼓励代码共享和尊重原作者的著作权, +允许代码修改,再作为开源或商业软件发布。需要满足 +的条件: +1. 需要给代码的用户一份Apache Licence ; +2. 如果你修改了代码,需要在被修改的文件中说明; +3. 在延伸的代码中(修改和有源代码衍生的代码中)需要 +带有原来代码中的协议,商标,专利声明和其他原来作者规 +定需要包含的说明; +4. 如果再发布的产品中包含一个Notice文件,则在Notice文 +件中需要带有本协议内容。你可以在Notice中增加自己的 +许可,但不可以表现为对Apache Licence构成更改。 +具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0 + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ae17b8 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +しら CDN V1.3 +=============== + +しら CDN是一个使用CloudFlare合作伙伴功能来为用户提供免费的Layer3/Layer4/Layer7防护和缓存加速功能的程序,并且集成了CloudXns为用户提供A记录解析,这是目前其他面板没有的,目前处于Beta阶段目前实现了以下功能: + + + CloudFlare免费套餐使用接入 + + CNAME记录加速 + + 借助CloudXns实现A记录加速 + + 免费的https(CloudFlare要求需要设置额外的验证CNAME) + + 免费使用CloudFlare的Railgun功能 + + http模式回源 + + 无限添加域名和子域 + + 可删除域名随时转到cloudflare官方或者其它合作伙伴 + + 可单独增删改某条记录 + + 支持A记录和AAAA记录 + +> しら CDN基于ThinkPHP开发,支持php5.4到7.1之间(推荐使用7.1,7.0,5.6) + + +## 部署相关 + +> 设置网站目录和入库目录说明 + +根目录则是整个程序目录,入口目录为/public. +不建议将public目录设置为网站目录,这样可能会带来不可预料的后果! +不建议将public以外的目录暴露在http可以访问的目录内,这样可能存在未知的安全隐患. + +> 伪静态设置 + +伪静态和ThinkPHP的伪静态一样,下面贴出nginx的伪静态设置,如果你的入口在二级或更深层的目录请自行修改伪静态配置. + +~~~ + location / { + if (!-e $request_filename){ + rewrite ^(.*)$ /index.php?s=$1 last; break; + } + } +~~~ +> 修改/application/config.php内的配置 + +~~~ + + 'webname'=>'しらCDN', //网站名称 + 'cf_api'=>'https://api.cloudflare.com/host-gw.html',//cloudflare经销商api,不需要修改 + 'cf_key'=>'', //cloudflare合作伙伴密钥 + 'forward_domain'=>'', //转发域名转发时使用的域名 + 'domain_id'=>0, //转发域名的cloudxns的id + 'xns_key'=>'', //cloudxns的API KEY + 'xns_secret'=>'', ///cloudxns的SECRET KEY + 'debug_level'=>0, //cloudflare的debug等级,一般不需要使用 + +~~~ +> 修改/application/database.php内的配置 + +~~~ + + // 服务器地址 + 'hostname' => '127.0.0.1', + // 数据库名 + 'database' => 'srk_cf', + // 用户名 + 'username' => 'root', + // 密码 + 'password' => '', + // 端口 + 'hostport' => '', +~~~ +> 导入import.sql + +不会导入的请从100M+的建筑物上跳下去,到地面之后你将知道如何导入. + +## 版本升级 +> 从1.2升级到1.3 + ++ 删除/public/static/index文件夹并使用1.3版本同目录替换 ++ 删除/application/index/controller/Panel.php并使用1.3版本同目录替换 ++ 将1.3版本/application/index/controller/Domain.php放置到同路径下 ++ 删除/application/index/view/panel目录使用1.3版本同目录替换 ++ 将1.3版本/application/index/view/domain和里面所有文件放置到同路径下 ++ MySQL执行下面语句即可完成升级. + +~~~ + ALTER TABLE `srk_domain` CHANGE `ip` `ip` VARCHAR(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL; +~~~ + + +## 版权信息 + +しらCDN遵循Apache2开源协议发布,并提供免费使用。 +你可以修改任何代码,但是你需要保留主页上的Power By 矢澤にこ字样和其链接(你可以增加不传递权重的标签). + +首页PV以及错误页面loding均引用[このは](https://www.conoha.jp/conoha/)的作品,是否允许使用,正在询问中. + +Power By [矢澤にこ](https://blog.ni-co.moe/) + +更多细节参阅 [LICENSE.txt](LICENSE.txt) diff --git a/application/command.php b/application/command.php new file mode 100644 index 0000000..826bb2b --- /dev/null +++ b/application/command.php @@ -0,0 +1,12 @@ + +// +---------------------------------------------------------------------- + +return []; diff --git a/application/common.php b/application/common.php new file mode 100644 index 0000000..2110c53 --- /dev/null +++ b/application/common.php @@ -0,0 +1,150 @@ + +// +---------------------------------------------------------------------- + +// 应用公共文件 + +// 执行请求 +function performRequest($data, $headers=NULL) { + $data["host_key"] = \think\Config::get('cf_key'); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, \think\Config::get('cf_api')); + $dlevel = \think\Config::get('debug_level'); + if ($dlevel > 1) { + curl_setopt($ch, CURLOPT_VERBOSE, 1); + } + if ($dlevel > 0) { + //echo "REQUEST DATA (to be sent via POST):\n"; + print_r($data); + } + + if ($headers) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + if ($data) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + } + + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); + curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + + if (($http_result = curl_exec($ch)) === FALSE) { + echo "WARNING: A connectivity error occured while contacting the service.\n"; + trigger_error(curl_error($ch)); + return FALSE; + } + + curl_close($ch); + // var_dump($data,$http_result);exit; + return $http_result; +} +//请求的过程 +function processResponse(& $response) { + $settings = NULL; + + if (PRINT_RESPONSE) { + echo "RAW RESPONSE DATA:\n".$response."\n"; + } + + echo "DECODED RESPONSE DATA:\n"; + $decoded_response = json_decode($response); + print_r($decoded_response); +} + +//返回信息检查 +function returnCheck($params){ + is_array($params) or $params = json_decode($params,true); + $errMsg = \think\Config::get('err_msg'); + if($params['result'] == 'error'){ + + if(!isset($errMsg[$params['err_code']])){ + // var_dump($params);exit; + return $params['err_code'].':'.$params['msg']; + }else{ + // var_dump($params);exit; + return $errMsg[$params['err_code']]; + } + + }else{ + return $params; + } +} + +//实例化一个xns的api +function newXnsApi(){ + $api = new \CloudXNS\Api(); + $api->setApiKey(\think\Config::get('xns_key')); + $api->setSecretKey(\think\Config::get('xns_secret')); + $api->setProtocol(true); + return $api; +} + +//添加转发记录 +function addForwardRecord($ip,$type = 'A',$api =null){ + $api or $api = newXnsApi(); + $did = \think\Config::get('domain_id'); + $host = md5(time().rand(1,9999)); + $return = $api->record->recordAdd($did, $host, $ip, $type , 10, 600, 1); + $return = json_decode($return,true); + // var_dump($type);exit; + // var_dump($return);exit; + if(isset($return['code']) && ($return['code'] == 34 || $return['code'] == 300 || $return['code'] == 403)){ + return false; + }else{ + return ['cname'=>$host.'.'.\think\Config::get('forward_domain'),'response'=>$return]; + } +} +//修改转发记录 +function updateForwardRecord($ip,$record_id,$api =null){ + $api or $api = newXnsApi(); + $did = \think\Config::get('domain_id'); + $api->record->recordUpdate($did, $host, $ip, 'A', 10, 600, 1, '', 854069); +} +//删除记录 +function delForwardRecord($record_id,$api =null){ + $api or $api = newXnsApi(); + $did = \think\Config::get('domain_id'); + return $api->record->recordDelete($record_id, $did); +} + +//ip转cname(调用添加转发记录) +function ip2cname($ip,$type = 'A'){ + $return = addForwardRecord($ip,$type); + // var_dump($return);exit; + if(!$return){ + return false; + }else{ + + return [ + 'ip'=>$ip, + 'record_id'=>$return['response']['record_id'][0], + 'cname'=>$return['cname'] + ]; + } +} + +function recordChick($data,$type = "A"){ + if($type =='A' || $type =='AAAA'){ + if(filter_var($data, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) && intval(substr($data,0,3)) != 127) { + return $data; + }else{ + return false; + } + }elseif($type == 'CNAME'){ + return true; + }else{ + return false; + } + +} \ No newline at end of file diff --git a/application/index/controller/Account.php b/application/index/controller/Account.php new file mode 100644 index 0000000..5a78d36 --- /dev/null +++ b/application/index/controller/Account.php @@ -0,0 +1,74 @@ +request = $request; + + } + + public function login(){ + if($this->request->isPost()){ + $data = [ + 'act'=>'user_auth', + 'cloudflare_email'=>$this->request->post('account'), + 'cloudflare_pass'=>$this->request->post('passwd'), + + ]; + + $account = \app\index\model\Account::instance(); + $uif = $account->findUser($data['cloudflare_email']); + + if(!$uif){ + $data['unique_id'] = md5(time().rand(1,9999)); + }else{ + $data['unique_id'] = $uif['unique_id']; + } + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + + if(!$uif){ + $uif = [ + 'email'=>$return['response']['cloudflare_email'], + 'username'=>isset($return['response']['cloudflare_username'])?$return['response']['cloudflare_username']:null, + 'unique_id'=>$return['response']['unique_id'], + 'user_key'=>$return['response']['user_key'], + 'user_api_key'=>$return['response']['user_api_key'] + ]; + $uif = $account->addUser($uif); + $msg = "登录成功!,如果您是第一次登录,您将会受到一封cloudflare的邮件,您的账户将会和我们关联."; + }else{ + $msg = "登录成功!."; + } + session::set('id',$uif['id']); + session::set('unique_id',$uif['unique_id']); + session::set('user_key',$return['response']['user_key']); + session::set('user_api_key',$return['response']['user_api_key']); + return $this->success($msg,'index/panel/index'); + }else{ + return $this->fetch(); + } + } + + public function logout(){ + session::delete('id'); + session::delete('unique_id'); + session::delete('user_key'); + session::delete('user_api_key'); + $this->redirect('index/account/login',302); + } + + +} \ No newline at end of file diff --git a/application/index/controller/Domain.php b/application/index/controller/Domain.php new file mode 100644 index 0000000..d751a50 --- /dev/null +++ b/application/index/controller/Domain.php @@ -0,0 +1,476 @@ +redirect('index/account/login',302); + } + self::$uif['unique_id'] = Session::get('unique_id'); + if(!self::$uif = self::$account->findUser(self::$uif['unique_id'],'unique_id')){ + $this->redirect('index/account/login',302); + } + $this->request = $request; + + } + + public function create(){ + + if($this->request->isPost()){ + if(!$this->request->has('zone_name','param')){ + return $this->error('没有设置主域!'); + } + $zone = $this->request->post('zone_name'); //取主域 + $domain = \app\index\model\Domain::instance(); + if($domain->getEarliestHost($zone)){ + return $this->error('这个域名已经被别人使用了!'); + } + $data = [ + 'act'=>'zone_set', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + 'subdomains'=>'', + ]; + $sqlData = []; + $sub = []; + $sub['type'] = $this->request->post('type/a'); + $sub['sub'] = $this->request->post('sub/a'); + $sub['record'] = $this->request->post('record/a'); + foreach($sub['type'] as $key=>$val){ + if(!$sub['type'][$key] || !$sub['sub'][$key] || !$sub['record'][$key]){ + unset($sub['type'][$key],$sub['sub'][$key],$sub['record'][$key]); + continue; + } + //当为泛域名时跳过(不允许添加泛域名) + if($sub['sub'][$key] =='*'){ + continue; + } + $recordData = []; + $recordData['unique_id'] = Session::get('unique_id'); + $recordData['domain'] = $zone; + $recordData['sub'] = $sub['sub'][$key]; + if($val == "A"||$val == "AAAA"){ + if(!recordChick($sub['record'][$key],$val)){ + return $this->error("子域名{$sub['sub'][$key]}的值不是一个IP!"); + } + if(!$cif = ip2cname($sub['record'][$key],$val)){ + return $this->error("转换子域名{$sub['sub'][$key]}时出现错误!"); + } + $recordData['type'] = $val; + $recordData['ip'] = $sub['record'][$key]; + if(!$cif){ + return $this->error("子域{$sub['sub'][$key]}在设置解析时发生错误,请联系管理员!"); + }else{ + $sub['record'][$key] = $cif['cname']; //使用cname覆盖原来的解析ip + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = $cif['record_id']; + } + }elseif($val == "CNAME"){ + $recordData['type'] = 'CNAME'; + $recordData['ip'] = null; + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = null; + }else{ + return $this->error('模式错误,我们只支持A|AAAA|CNAME模式.'); + } + + if($sub['sub'][$key] == '@'){ + $data['resolve_to'] = $recordData['cname']; + $sqlData['_www'] = $recordData; + $sqlData['_www']['sub'] = 'www'; + }else{ + $data['subdomains'] .= "{$recordData['sub']}:{$sub['record'][$key]},"; + } + $sqlData[] = $recordData; + + unset($recordData); + } + $data['subdomains'] = substr($data['subdomains'], 0, -1); + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + if(in_array('www',$sub['sub'])){ + unset($sqlData['_www']); //当子域包含www的时候删除默认值 + } + $domain->addMultiDomainInfo($sqlData); + return $this->success('域名添加成功',url('index/domain/info',['domain'=>$zone])); + }else{ + return $this->fetch(); + } + + } + + public function info(){ + if(!$this->request->has('domain','param')){ + $this->error('参数错误!','index/panel/index'); + } + $zone = $this->request->param('domain'); + $return = $this->getDomainList($zone); + if(!is_array($return)){ + return $this->error($return); + } + if(!$return['response']['zone_exists']){ + return $this->error('这个域名已被删除或不属于你!'); + } + $sub = []; + //添加域名对应cname + foreach($return["response"]['forward_tos'] as $key=>$val){ + $sub[$key]['cname'] = $val; + } + $forwardDomain = \think\Config::get('forward_domain');//读取转发域名 + $domain = null; + //添加域名对应源地址 + foreach($return["response"]['hosted_cnames'] as $key=>$val){ + if($key == $zone){ + $sub[$key]['sub'] = '@'; + }else{ + $sub[$key]['sub'] = substr($key,0,strlen($key) - strlen($zone) - 1);//去除根域得到子域 + } + + if(substr($val,-12) == 'comodoca.com'){//如果回源域名是comodo验证域 + $sub[$key]['cname'] = $val; + $sub[$key]['source'] = ""; + }elseif(substr($val, 0 - strlen($forwardDomain)) == $forwardDomain){ //如果源cname来自转发域名 + $domain = \app\index\model\Domain::instance($domain); + $source = $domain->findHost($sub[$key]['sub'],$zone); + if(!$source){ //没有从本地记录里找到解析记录 + $sub[$key]['source'] = $val; + }elseif($source['type'] == 'A' || $source['type'] == 'AAAA'){ //如果本地记录是A记录 + $sub[$key]['source'] = $source['ip']; + }elseif($source['type'] == 'CNAME'){ //如果本地记录是CNAME记录 + $sub[$key]['source'] = $val; + } + }else{ + $sub[$key]['source'] = $val; + } + } + $this->assign('sub',$sub); + $this->assign('domain',$zone); + return $this->fetch(); + + } + + public function edit(){ + if(!$this->request->has('domain','param') || !$this->request->has('sub','param')){ + $this->error('参数错误!','index/panel/index'); + } + $domain = \app\index\model\Domain::instance(); + $sub = $this->request->param('sub'); + $zone = $this->request->param('domain'); + if($this->request->isPost()){ + $type = $this->request->param('type'); + $record = $this->request->param('record'); + $allHost = $domain->findDomain($zone,Session::get('unique_id')); + $subHostInfo = []; //用于后面的更新记录使用 + $sourceHostInfo = []; //源记录,用于请求成功删除用 + $data = [ + 'act'=>'zone_set', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + 'subdomains'=>'', + ]; + foreach($allHost as $key=>$val){ + if($val['sub'] != $sub){ //子域不是要修改的子域名时 + if($val['sub'] =='@'){ + $data['resolve_to'] = $val['cname']; //如果是顶级域名就设置顶级域名解析 + }else{ + $data['subdomains'] .= "{$val['sub']}:{$val['cname']},"; //如果是自己域名则添加到子域 + } + + }else{ + $subHostInfo = $sourceHostInfo = $val; //先将现有数据另存2份 + unset($subHostInfo['id']); + if($type == 'A' || $type == 'AAAA'){ + if(!recordChick($record,$type)){ + return $this->error("{$record}的值不是一个IP!"); + } + if(!$cif = ip2cname($record,$type)){ + return $this->error("转换子域名时出现错误!"); + } + if($val['sub'] == '@'){ + $data['resolve_to'] = $cif['cname']; + }else{ + $data['subdomains'] .= "{$val['sub']}:{$cif['cname']},"; //将解析的cname设置为新的cname + } + $subHostInfo['type'] = $type; + $subHostInfo['ip'] = $record; + $subHostInfo['cname'] = $cif['cname']; + $subHostInfo['record_id'] =$cif['record_id']; + }elseif($type =='CNAME'){ + if($val['sub'] == '@'){ + $data['resolve_to'] = $record; + //continue; //当域名是主域名时设置解析值后跳出本次循环 + }else{ + $data['subdomains'] .= "{$val['sub']}:{$record},"; //将解析的cname设置为新的cname + } + $subHostInfo['type'] = 'CNAME'; + $subHostInfo['ip'] = null; + $subHostInfo['cname'] = $record; + $subHostInfo['record_id'] = null; + }else{ + return $this->error('模式错误,我们只支持A模式和CNAME模式.'); + } + } + } + + $comodo = $this->getComodoSub($zone); //获取comodossl + if($comodo['code'] == 1){ + $data['subdomains'] .=$comodo['data']; //如果有comodo域名 + }elseif($comodo['code'] == 0){ + $data['subdomains'] = substr($data['subdomains'], 0, -1); //去除最后一个标点 + }else{ + return $this->error($comodo); + } + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + $domain->addDomainInfo($subHostInfo); //添加新的解析记录 + if($sourceHostInfo){ + $domain->delForwardRecord($sourceHostInfo['id']); //删除原来的解析记录 + delForwardRecord($sourceHostInfo['record_id']); //删除xns原来的解析 + } + Cache::rm('cf_'.$zone); + return $this->success('解析记录更新成功!',url('index/domain/info',['domain'=>$zone])); + }else{ + $dif = $domain->findHost($sub,$zone); + $this->assign('dif',$dif); + return $this->fetch(); + } + } + + public function del(){ + if(!$this->request->has('domain','param') || !$this->request->has('sub','param')){ + $this->error('参数错误!','index/panel/index'); + } + $sub = $this->request->param('sub'); + $zone = $this->request->param('domain'); + + if($sub =='@'){ + return $this->error('你不能删除顶级域名的解析记录.'); + } + $data = [ + 'act'=>'zone_set', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + 'subdomains'=>'', + ]; + $domain = \app\index\model\Domain::instance(); + $allHost = $domain->findDomain($zone,Session::get('unique_id')); //取用户所有列表 + if(!$allHost){ + return $this->error('没有这个域名的解析记录或者这个域名不属于你!'); + } + foreach($allHost as $key=>$val){ + if($val['sub'] == $sub){ + $delId['id'] = $val['id']; + if($val['type'] =='A' || $val['type'] =='AAAA'){ + $delId['xid'] = $val['record_id']; + } + }else{ + if($val['sub'] == '@'){ + $data['resolve_to'] = $val['cname']; + }else{ + $data['subdomains'] .= "{$val['sub']}:{$val['cname']},"; + } + } + } + if(!isset($delId)){ + return $this->error('没有找到需要删除的记录!'); + } + $comodo = $this->getComodoSub($zone); //获取comodossl + if($comodo['code'] == 1){ + $data['subdomains'] .=$comodo['data']; //如果有comodo域名 + }elseif($comodo['code'] == 0){ + $data['subdomains'] = substr($data['subdomains'], 0, -1); //去除最后一个标点 + }else{ + return $this->error($comodo); + } + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + if(isset($delId['xid'])){ + delForwardRecord($delId['xid']); + } + $domain->delForwardRecord($delId['id']); + Cache::rm('cf_'.$zone); + return $this->success('记录成功删除'); + } + + //获取域名下的子域名 + private function getDomainList($zone){ + if(!$return = Cache::get('cf_'.$zone)){ + $data = [ + 'act'=>'zone_lookup', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + ]; + $return = performRequest($data); + $return = returnCheck($return); + if(is_array($return)){ + Cache::set('cf_'.$zone,$return); + } + } + return $return; + + } + + public function add(){ + if($this->request->isGet()){ + return $this->error('你好像走错路了'); + } + if(!$this->request->has('domain','param')){ + $this->error('参数错误!','index/panel/index'); + } + $zone = $this->request->param('domain'); + $domain = \app\index\model\Domain::instance(); + if(!$allHost = $domain->findDomain($zone,Session::get('unique_id'))){ + return $this->error('您的帐户下不存在这个域名!'); + } + $data = [ + 'act'=>'zone_set', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + 'subdomains'=>'', + ]; + $subList = []; + //将sql里所有记录的子域全部取出并添加相应的子域记录 + foreach($allHost as $key=>$val){ + $subList [] = $val['sub']; + if($val['sub'] == '@'){ + $data['resolve_to'] = $val['cname']; + }else{ + $data['subdomains'] .= $val['sub'].':'.$val['cname'].','; + } + + } + $sqlData = []; //最后插入sql + //获取新添加的子域记录 + $sub = []; + $sub['type'] = $this->request->post('type/a'); + $sub['sub'] = $this->request->post('sub/a'); + $sub['record'] = $this->request->post('record/a'); + foreach($sub['type'] as $key=>$val){ + //去除无效的解析记录 + if(!$sub['type'][$key] || !$sub['sub'][$key] || !$sub['record'][$key]){ + unset($sub['type'][$key],$sub['sub'][$key],$sub['record'][$key]); + continue; + } + //当为顶级域和泛域名时跳过(添加子域解析不允许添加顶级域名解析) + if(in_array($sub['sub'][$key],$subList) || $sub['sub'][$key] =='*'){ + continue; + } + $recordData = []; + $recordData['unique_id'] = Session::get('unique_id'); + $recordData['domain'] = $zone; + $recordData['sub'] = $sub['sub'][$key]; + if($val == "A" || $val == "AAAA"){ + if(!recordChick($sub['record'][$key],$val)){ + return $this->error("子域名{$sub['sub'][$key]}的值不是一个IP!"); + } + if(!$cif = ip2cname($sub['record'][$key],$val)){ + return $this->error("转换子域名{$sub['sub'][$key]}时出现错误!"); + } + $recordData['type'] = $val; + $recordData['ip'] = $sub['record'][$key]; + if(!$cif){ + return $this->error("子域{$sub['sub'][$key]}在设置解析时发生错误,请联系管理员!"); + }else{ + $sub['record'][$key] = $cif['cname']; //使用cname覆盖原来的解析ip + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = $cif['record_id']; + } + }elseif($val == "CNAME"){ + $recordData['type'] = 'CNAME'; + $recordData['ip'] = null; + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = null; + }else{ + return $this->error('模式错误,我们只支持A|AAAA|CNAME模式.'); + } + $data['subdomains'] .= "{$recordData['sub']}:{$sub['record'][$key]},"; + $sqlData[] = $recordData; + unset($recordData); + } + + //添加comodo验证域名 + $comodo = $this->getComodoSub($zone); //获取comodossl + if($comodo['code'] == 1){ + $data['subdomains'] .=$comodo['data']; //如果有comodo域名 + }elseif($comodo['code'] == 0){ + $data['subdomains'] = substr($data['subdomains'], 0, -1); //去除最后一个标点 + }else{ + return $this->error($comodo); + } + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + $domain->addMultiDomainInfo($sqlData); + Cache::rm('cf_'.$zone); + return $this->success('子域名记录添加成功',url('index/domain/info',['domain'=>$zone])); + } + + public function deldomain(){ + $zone = $this->request->param('domain'); + $data = [ + 'act'=>'zone_delete', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone + ]; + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + $unique_id = Session::get('unique_id'); + $domain = \app\index\model\Domain::instance(); + $subList = $domain->delAllDomainInfo($zone); + + return $this->success('域名删除成功!'); + } + + private function getComodoSub($zone,$sub = true){ + if(!$lastDomain =Cache::get('comodo-'.$zone)){ + $lastDomain = $this->getDomainList($zone); + if(!is_array($lastDomain)){ + return ['code'=>-1,'data'=>$this->error($return)]; + } + } + foreach($lastDomain['response']['hosted_cnames'] as $key=>$val){ + if(substr($val,-12) == 'comodoca.com'){//如果回源域名是comodo验证域 + Cache::set('comodo-'.$zone,$lastDomain); //取到comodo域名的时候缓存记录 + if($sub){ + $sub = substr($key,0,strlen($key) - strlen($zone) - 1); + return ['code'=>1,'data'=>$sub.':'.$val]; + }else{ + $sub = substr($key,0,strlen($key) - strlen($zone) - 1); + return ['code'=>1,'data'=>['sub'=>$sub,'record'=>$val]]; + } + break; + } + } + return ['code'=>0,'data'=>null]; + } + +} \ No newline at end of file diff --git a/application/index/controller/Index.php b/application/index/controller/Index.php new file mode 100644 index 0000000..368e9fd --- /dev/null +++ b/application/index/controller/Index.php @@ -0,0 +1,11 @@ +fetch(); + } +} diff --git a/application/index/controller/Panel.php b/application/index/controller/Panel.php new file mode 100644 index 0000000..8dea581 --- /dev/null +++ b/application/index/controller/Panel.php @@ -0,0 +1,192 @@ +redirect('index/account/login',302); + } + self::$uif['unique_id'] = Session::get('unique_id'); + if(!self::$uif = self::$account->findUser(self::$uif['unique_id'],'unique_id')){ + $this->redirect('index/account/login',302); + } + $this->request = $request; + + } + + public function index(){ + $data = [ + 'act'=>'user_lookup', + // 'user_key'=>Session::get('user_key'), + 'unique_id'=>self::$uif['unique_id'], + 'cloudflare_email'=>self::$uif['email'], + ]; + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + $domain = $return["response"]["hosted_zones"]; + $this->assign('domain',$domain); + return $this->fetch(); + } + + public function adddomain(){ + + if($this->request->isPost()){ + if(!$this->request->has('zone_name','param')){ + return $this->error('没有设置主域!'); + } + $zone = $this->request->post('zone_name'); //取主域 + $domain = \app\index\model\Domain::instance(); + if($domain->getEarliestHost($zone)){ + return $this->error('这个域名已经被别人使用了!'); + } + $data = [ + 'act'=>'zone_set', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + 'subdomains'=>'', + ]; + $sqlData = []; + $sub = []; + $sub['type'] = $this->request->post('type/a'); + $sub['sub'] = $this->request->post('sub/a'); + $sub['record'] = $this->request->post('record/a'); + foreach($sub['type'] as $key=>$val){ + if(!$sub['type'][$key] || !$sub['sub'][$key] || !$sub['record'][$key]){ + unset($sub['type'][$key],$sub['sub'][$key],$sub['record'][$key]); + continue; + } + //当为泛域名时跳过(不允许添加泛域名) + if($sub['sub'][$key] =='*'){ + continue; + } + $recordData = []; + $recordData['unique_id'] = Session::get('unique_id'); + $recordData['domain'] = $zone; + $recordData['sub'] = $sub['sub'][$key]; + if($val == "A"){ + if(!recordChick($sub['record'][$key],'A')){ + return $this->error("子域名{$sub['sub'][$key]}的值不是一个IP!"); + } + if(!$cif = ip2cname($sub['record'][$key])){ + return $this->error("转换子域名{$sub['sub'][$key]}时出现错误!"); + } + $recordData['type'] = 'A'; + $recordData['ip'] = $sub['record'][$key]; + if(!$cif){ + return $this->error("子域{$sub['sub'][$key]}在设置解析时发生错误,请联系管理员!"); + }else{ + $sub['record'][$key] = $cif['cname']; //使用cname覆盖原来的解析ip + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = $cif['record_id']; + } + }elseif($val == "CNAME"){ + $recordData['type'] = 'CNAME'; + $recordData['ip'] = null; + $recordData['cname'] = $sub['record'][$key]; + $recordData['record_id'] = null; + }else{ + return $this->error('模式错误,我们只支持A模式和CNAME模式.'); + } + + if($sub['sub'][$key] == '@'){ + $data['resolve_to'] = $recordData['cname']; + $sqlData['_www'] = $recordData; + $sqlData['_www']['sub'] = 'www'; + }else{ + $data['subdomains'] .= "{$recordData['sub']}:{$sub['record'][$key]},"; + } + $sqlData[] = $recordData; + + unset($recordData); + } + $data['subdomains'] = substr($data['subdomains'], 0, -1); + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + if(in_array('www',$sub['sub'])){ + unset($sqlData['_www']); //当子域包含www的时候删除默认值 + } + $domain->addMultiDomainInfo($sqlData); + return $this->success('域名添加成功','index/panel/index'); + }else{ + return $this->fetch(); + } + + } + + public function deldomain(){ + $zone = $this->request->param('domain'); + $data = [ + 'act'=>'zone_delete', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone + ]; + $return = performRequest($data); + $return = returnCheck($return); + if(!is_array($return)){ + return $this->error($return); + } + $unique_id = Session::get('unique_id'); + $domain = \app\index\model\Domain::instance(); + $subList = $domain->delAllDomainInfo($zone); + Cache::rm('cf_'.$zone); + return $this->success('域名删除成功!'); + } + + + //获取域名下的子域名 + private function getDomainList($zone){ + $data = [ + 'act'=>'zone_lookup', + 'user_key'=>Session::get('user_key'), + 'zone_name'=>$zone, + ]; + $return = performRequest($data); + $return = returnCheck($return); + return $return; + + } + + private function getComodoSub($zone,$sub = true){ + if(!$lastDomain =Cache::get('comodo-'.$zone)){ + $lastDomain = $this->getDomainList($zone); + if(!is_array($lastDomain)){ + return ['code'=>-1,'data'=>$this->error($return)]; + } + } + foreach($lastDomain['response']['hosted_cnames'] as $key=>$val){ + if(substr($val,-12) == 'comodoca.com'){//如果回源域名是comodo验证域 + Cache::set('comodo-'.$zone,$lastDomain); //取到comodo域名的时候缓存记录 + if($sub){ + $sub = substr($key,0,strlen($key) - strlen($zone) - 1); + return ['code'=>1,'data'=>$sub.':'.$val]; + }else{ + $sub = substr($key,0,strlen($key) - strlen($zone) - 1); + return ['code'=>1,'data'=>['sub'=>$sub,'record'=>$val]]; + } + break; + } + } + return ['code'=>0,'data'=>null]; + } +} \ No newline at end of file diff --git a/application/index/model/Account.php b/application/index/model/Account.php new file mode 100644 index 0000000..d728819 --- /dev/null +++ b/application/index/model/Account.php @@ -0,0 +1,41 @@ +where([$type=>$user])->find(); + } + + public function adduser($data,$db = null){ + $db or $db = Db::name('user'); + $data['carte_time'] = time(); + if($data ['id'] = $db->insertGetId($data)){ + + return $data; + }else{ + return false; + } + } + + public function setUser($data,$db = null){ + $db or $db = Db::name('user'); + $db = $db->where($data['id']); + unset($data['id'],$data['email'],$data['carte_time']); + return $db->update($data); + } + + +} \ No newline at end of file diff --git a/application/index/model/Domain.php b/application/index/model/Domain.php new file mode 100644 index 0000000..491d491 --- /dev/null +++ b/application/index/model/Domain.php @@ -0,0 +1,52 @@ +where(['domain'=>$zname,'sub'=>$sub])->find(); + } + + public function getEarliestHost($zone,$db = null){ + $db or $db = Db::name('domain'); + return $db->where(['domain'=>$zone])->order('id asc')->find(); + } + + public function addDomainInfo($data,$db = null){ + $db or $db = Db::name('domain'); + return $db->insert($data); + } + + public function addMultiDomainInfo($data,$db = null){ + $db or $db = Db::name('domain'); + return $db->insertAll($data); + } + + public function findDomain($zone,$unique_id,$db = null){ + $db or $db = Db::name('domain'); + return $db->where(['domain'=>$zone,'unique_id'=>$unique_id])->select(); + } + + public function delForwardRecord($id,$db = null){ + $db or $db = Db::name('domain'); + return $db->where(['id'=>$id])->delete(); + } + + public function delAllDomainInfo($domain,$db = null){ + $db or $db = Db::name('domain'); + return $db->where(['domain'=>$domain])->delete(); + } +} \ No newline at end of file diff --git a/application/index/view/account/login.html b/application/index/view/account/login.html new file mode 100644 index 0000000..2e2183f --- /dev/null +++ b/application/index/view/account/login.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + {:config('webname')} - 账户登录 + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + diff --git a/application/index/view/domain/create.html b/application/index/view/domain/create.html new file mode 100644 index 0000000..e9c5fd8 --- /dev/null +++ b/application/index/view/domain/create.html @@ -0,0 +1,204 @@ + + + + + + + + + + + {:config('webname')} - 添加新域名 + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+

添加新域名

+
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+ 主域名 + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
类型子域名解析值操作
+ +
删除
+ +
删除
+ +
+
+
增加一行
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + diff --git a/application/index/view/domain/edit.html b/application/index/view/domain/edit.html new file mode 100644 index 0000000..7a12f5d --- /dev/null +++ b/application/index/view/domain/edit.html @@ -0,0 +1,151 @@ + + + + + + + + + + + {:config('webname')} - 编辑子域解析记录 + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+

{$dif.domain}

+
+ +
+ +
+
+
+
+ 修改子域名{$dif.sub} +
+
+
+
+
+ + + + + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + diff --git a/application/index/view/domain/info.html b/application/index/view/domain/info.html new file mode 100644 index 0000000..2dee61e --- /dev/null +++ b/application/index/view/domain/info.html @@ -0,0 +1,246 @@ + + + + + + + + + + + + {:config('webname')} - 子域解析列表 + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+

域名解析列表

+
+ +
+ +
+
+
+
+ {$domain} 的解析记录 +
+ +
+
+ + + + + + + + + + + {volist name="sub" id="vo"} + + + + + + + {/volist} + +
域名CNAME源地址操作
{$vo.sub}{$vo.cname}{$vo.source}修改 删除
+
+ +
添加新子域记录
+
+ +
+ + + + +
+ +
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/index/view/index/index.html b/application/index/view/index/index.html new file mode 100644 index 0000000..4af0304 --- /dev/null +++ b/application/index/view/index/index.html @@ -0,0 +1,97 @@ + + + + + + + + + + + {:config('webname')} + + + + + + + + + + + + + + + + + + +
+
+ +
请等待,加载中...
+
+
+
+
+
+
+
+
+
+
+
+ Slide 1 +

+ + - + We are + {:config('webname')} + +

+
+
+

+ 矢澤 + にこ +

+
+
+

+ 用户 + 中心 +

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/application/index/view/panel/index.html b/application/index/view/panel/index.html new file mode 100644 index 0000000..7995fce --- /dev/null +++ b/application/index/view/panel/index.html @@ -0,0 +1,167 @@ + + + + + + + + + + + {:config('webname')} - 域名列表 + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+

域名列表

+
+ +
+ +
+
+
+
+ 账户中所有的域名 +
+ +
+ + + + + + + + + + {volist name="domain" id="vo"} + + + + + + {/volist} + +
域名查看解析列表删除域名
{$vo}管理删除
+ + +
+ +
+ +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/application/route.php b/application/route.php new file mode 100644 index 0000000..f648d3b --- /dev/null +++ b/application/route.php @@ -0,0 +1,21 @@ + +// +---------------------------------------------------------------------- + +return [ + '__pattern__' => [ + 'name' => '\w+', + ], + '[hello]' => [ + ':id' => ['index/hello', ['method' => 'get'], ['id' => '\d+']], + ':name' => ['index/hello', ['method' => 'post']], + ], + +]; diff --git a/application/tags.php b/application/tags.php new file mode 100644 index 0000000..e213e0a --- /dev/null +++ b/application/tags.php @@ -0,0 +1,28 @@ + +// +---------------------------------------------------------------------- + +// 应用行为扩展定义文件 +return [ + // 应用初始化 + 'app_init' => [], + // 应用开始 + 'app_begin' => [], + // 模块初始化 + 'module_init' => [], + // 操作开始执行 + 'action_begin' => [], + // 视图内容过滤 + 'view_filter' => [], + // 日志写入 + 'log_write' => [], + // 应用结束 + 'app_end' => [], +]; diff --git a/application/tpl/dispatch_jump.html b/application/tpl/dispatch_jump.html new file mode 100644 index 0000000..018ee25 --- /dev/null +++ b/application/tpl/dispatch_jump.html @@ -0,0 +1,68 @@ + + + + + 提示 + + + + + +

+

+ 秒后跳转 +

+ + + + \ No newline at end of file diff --git a/build.php b/build.php new file mode 100644 index 0000000..b37d3ab --- /dev/null +++ b/build.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- + +return [ + // 生成应用公共文件 + '__file__' => ['common.php', 'config.php', 'database.php'], + + // 定义demo模块的自动生成 (按照实际定义的文件名生成) + 'demo' => [ + '__file__' => ['common.php'], + '__dir__' => ['behavior', 'controller', 'model', 'view'], + 'controller' => ['Index', 'Test', 'UserType'], + 'model' => ['User', 'UserType'], + 'view' => ['index/index'], + ], + // 其他更多的模块定义 +]; diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..320e8ef --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "topthink/think", + "description": "the new thinkphp framework", + "type": "project", + "keywords": [ + "framework", + "thinkphp", + "ORM" + ], + "homepage": "http://thinkphp.cn/", + "license": "Apache-2.0", + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "require": { + "php": ">=5.4.0", + "topthink/framework": "^5.0", + "cloudxns/cloud-xns-api-sdk-php": "*", + "guzzlehttp/guzzle": " ~5.0" + }, + "extra": { + "think-path": "thinkphp" + }, + "config": { + "preferred-install": "dist" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..f57316d --- /dev/null +++ b/composer.lock @@ -0,0 +1,349 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "6504bc784d76ccf75ab40ca253224a8b", + "content-hash": "8a5099c3a2a9d6c72761a83b3187bca8", + "packages": [ + { + "name": "cloudxns/cloud-xns-api-sdk-php", + "version": "V1.0", + "source": { + "type": "git", + "url": "https://github.com/CloudXNS/CloudXNS-API-SDK-PHP.git", + "reference": "77cef883d6c6d348d3dc1c6edf51dac10f90e198" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CloudXNS/CloudXNS-API-SDK-PHP/zipball/77cef883d6c6d348d3dc1c6edf51dac10f90e198", + "reference": "77cef883d6c6d348d3dc1c6edf51dac10f90e198", + "shasum": "" + }, + "require-dev": { + "guzzlehttp/guzzle": "~5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "CloudXNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CloudXNS", + "email": "support@cloudxns.net" + } + ], + "description": "CloudXNS SDK FILES", + "homepage": "https://www.cloudxns.net/", + "time": "2016-01-26 09:30:53" + }, + { + "name": "guzzlehttp/guzzle", + "version": "5.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/70f1fa53b71c4647bf2762c09068a95f77e12fb8", + "reference": "70f1fa53b71c4647bf2762c09068a95f77e12fb8", + "shasum": "" + }, + "require": { + "guzzlehttp/ringphp": "^1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2016-07-15 19:28:39" + }, + { + "name": "guzzlehttp/ringphp", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/RingPHP.git", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "shasum": "" + }, + "require": { + "guzzlehttp/streams": "~3.0", + "php": ">=5.4.0", + "react/promise": "~2.0" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "ext-curl": "Guzzle will use specific adapters if cURL is present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Ring\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "time": "2015-05-20 03:37:09" + }, + { + "name": "guzzlehttp/streams", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/streams.git", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Provides a simple abstraction over streams of data", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "Guzzle", + "stream" + ], + "time": "2014-10-12 19:18:40" + }, + { + "name": "react/promise", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "62785ae604c8d69725d693eb370e1d67e94c4053" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053", + "reference": "62785ae604c8d69725d693eb370e1d67e94c4053", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "time": "2017-03-25 12:08:31" + }, + { + "name": "topthink/framework", + "version": "v5.0.10", + "source": { + "type": "git", + "url": "https://github.com/top-think/framework.git", + "reference": "e428c43ec5138c989cfdb38ac300a964ad77f9cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/framework/zipball/e428c43ec5138c989cfdb38ac300a964ad77f9cb", + "reference": "e428c43ec5138c989cfdb38ac300a964ad77f9cb", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "topthink/think-installer": "~1.0" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.0", + "mikey179/vfsstream": "~1.6", + "phpdocumentor/reflection-docblock": "^2.0", + "phploc/phploc": "2.*", + "phpunit/phpunit": "4.8.*", + "sebastian/phpcpd": "2.*" + }, + "type": "think-framework", + "autoload": { + "psr-4": { + "think\\": "library/think" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + } + ], + "description": "the new thinkphp framework", + "homepage": "http://thinkphp.cn/", + "keywords": [ + "framework", + "orm", + "thinkphp" + ], + "time": "2017-07-05 03:35:06" + }, + { + "name": "topthink/think-installer", + "version": "v1.0.12", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-installer.git", + "reference": "1be326e68f63de4e95977ed50f46ae75f017556d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-installer/zipball/1be326e68f63de4e95977ed50f46ae75f017556d", + "reference": "1be326e68f63de4e95977ed50f46ae75f017556d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev" + }, + "type": "composer-plugin", + "extra": { + "class": "think\\composer\\Plugin" + }, + "autoload": { + "psr-4": { + "think\\composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "time": "2017-05-27 06:58:09" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.4.0" + }, + "platform-dev": [] +} diff --git a/import.sql b/import.sql new file mode 100644 index 0000000..c2f3ff6 --- /dev/null +++ b/import.sql @@ -0,0 +1,28 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +CREATE TABLE IF NOT EXISTS `srk_domain` ( + `id` int(11) NOT NULL, + `unique_id` varchar(32) NOT NULL, + `domain` tinytext NOT NULL, + `sub` tinytext NOT NULL, + `type` varchar(5) NOT NULL, + `ip` varchar(40) DEFAULT NULL, + `cname` text NOT NULL, + `record_id` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `srk_domain` + ADD PRIMARY KEY (`id`); + +ALTER TABLE `srk_domain` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..cbc7868 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,8 @@ + + Options +FollowSymlinks -Multiviews + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..53fe3bc Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..6b7ec30 --- /dev/null +++ b/public/index.php @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- + +// [ 应用入口文件 ] + +// 定义应用目录 +define('APP_PATH', __DIR__ . '/../application/'); +// 加载框架引导文件 +require __DIR__ . '/../thinkphp/start.php'; diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/router.php b/public/router.php new file mode 100644 index 0000000..6ba1fab --- /dev/null +++ b/public/router.php @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +// $Id$ + +if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["REQUEST_URI"])) { + return false; +} else { + require __DIR__ . "/index.php"; +} diff --git a/public/static/dist/css/sb-admin-2.css b/public/static/dist/css/sb-admin-2.css new file mode 100644 index 0000000..ef5e53b --- /dev/null +++ b/public/static/dist/css/sb-admin-2.css @@ -0,0 +1,434 @@ +/*! + * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2016 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) + */ +body { + background-color: #f8f8f8; +} +#wrapper { + width: 100%; +} +#page-wrapper { + padding: 0 15px; + min-height: 568px; + background-color: white; +} +@media (min-width: 768px) { + #page-wrapper { + position: inherit; + margin: 0 0 0 250px; + padding: 0 30px; + border-left: 1px solid #e7e7e7; + } +} +.navbar-top-links { + margin-right: 0; +} +.navbar-top-links li { + display: inline-block; +} +.navbar-top-links li:last-child { + margin-right: 15px; +} +.navbar-top-links li a { + padding: 15px; + min-height: 50px; +} +.navbar-top-links .dropdown-menu li { + display: block; +} +.navbar-top-links .dropdown-menu li:last-child { + margin-right: 0; +} +.navbar-top-links .dropdown-menu li a { + padding: 3px 20px; + min-height: 0; +} +.navbar-top-links .dropdown-menu li a div { + white-space: normal; +} +.navbar-top-links .dropdown-messages, +.navbar-top-links .dropdown-tasks, +.navbar-top-links .dropdown-alerts { + width: 310px; + min-width: 0; +} +.navbar-top-links .dropdown-messages { + margin-left: 5px; +} +.navbar-top-links .dropdown-tasks { + margin-left: -59px; +} +.navbar-top-links .dropdown-alerts { + margin-left: -123px; +} +.navbar-top-links .dropdown-user { + right: 0; + left: auto; +} +.sidebar .sidebar-nav.navbar-collapse { + padding-left: 0; + padding-right: 0; +} +.sidebar .sidebar-search { + padding: 15px; +} +.sidebar ul li { + border-bottom: 1px solid #e7e7e7; +} +.sidebar ul li a.active { + background-color: #eeeeee; +} +.sidebar .arrow { + float: right; +} +.sidebar .fa.arrow:before { + content: "\f104"; +} +.sidebar .active > a > .fa.arrow:before { + content: "\f107"; +} +.sidebar .nav-second-level li, +.sidebar .nav-third-level li { + border-bottom: none !important; +} +.sidebar .nav-second-level li a { + padding-left: 37px; +} +.sidebar .nav-third-level li a { + padding-left: 52px; +} +@media (min-width: 768px) { + .sidebar { + z-index: 1; + position: absolute; + width: 250px; + margin-top: 51px; + } + .navbar-top-links .dropdown-messages, + .navbar-top-links .dropdown-tasks, + .navbar-top-links .dropdown-alerts { + margin-left: auto; + } +} +.btn-outline { + color: inherit; + background-color: transparent; + transition: all .5s; +} +.btn-primary.btn-outline { + color: #428bca; +} +.btn-success.btn-outline { + color: #5cb85c; +} +.btn-info.btn-outline { + color: #5bc0de; +} +.btn-warning.btn-outline { + color: #f0ad4e; +} +.btn-danger.btn-outline { + color: #d9534f; +} +.btn-primary.btn-outline:hover, +.btn-success.btn-outline:hover, +.btn-info.btn-outline:hover, +.btn-warning.btn-outline:hover, +.btn-danger.btn-outline:hover { + color: white; +} +.chat { + margin: 0; + padding: 0; + list-style: none; +} +.chat li { + margin-bottom: 10px; + padding-bottom: 5px; + border-bottom: 1px dotted #999999; +} +.chat li.left .chat-body { + margin-left: 60px; +} +.chat li.right .chat-body { + margin-right: 60px; +} +.chat li .chat-body p { + margin: 0; +} +.panel .slidedown .glyphicon, +.chat .glyphicon { + margin-right: 5px; +} +.chat-panel .panel-body { + height: 350px; + overflow-y: scroll; +} +.login-panel { + margin-top: 25%; +} +.flot-chart { + display: block; + height: 400px; +} +.flot-chart-content { + width: 100%; + height: 100%; +} +table.dataTable thead .sorting, +table.dataTable thead .sorting_asc, +table.dataTable thead .sorting_desc, +table.dataTable thead .sorting_asc_disabled, +table.dataTable thead .sorting_desc_disabled { + background: transparent; +} +table.dataTable thead .sorting_asc:after { + content: "\f0de"; + float: right; + font-family: fontawesome; +} +table.dataTable thead .sorting_desc:after { + content: "\f0dd"; + float: right; + font-family: fontawesome; +} +table.dataTable thead .sorting:after { + content: "\f0dc"; + float: right; + font-family: fontawesome; + color: rgba(50, 50, 50, 0.5); +} +.btn-circle { + width: 30px; + height: 30px; + padding: 6px 0; + border-radius: 15px; + text-align: center; + font-size: 12px; + line-height: 1.428571429; +} +.btn-circle.btn-lg { + width: 50px; + height: 50px; + padding: 10px 16px; + border-radius: 25px; + font-size: 18px; + line-height: 1.33; +} +.btn-circle.btn-xl { + width: 70px; + height: 70px; + padding: 10px 16px; + border-radius: 35px; + font-size: 24px; + line-height: 1.33; +} +.show-grid [class^="col-"] { + padding-top: 10px; + padding-bottom: 10px; + border: 1px solid #ddd; + background-color: #eee !important; +} +.show-grid { + margin: 15px 0; +} +.huge { + font-size: 40px; +} +.panel-green { + border-color: #5cb85c; +} +.panel-green > .panel-heading { + border-color: #5cb85c; + color: white; + background-color: #5cb85c; +} +.panel-green > a { + color: #5cb85c; +} +.panel-green > a:hover { + color: #3d8b3d; +} +.panel-red { + border-color: #d9534f; +} +.panel-red > .panel-heading { + border-color: #d9534f; + color: white; + background-color: #d9534f; +} +.panel-red > a { + color: #d9534f; +} +.panel-red > a:hover { + color: #b52b27; +} +.panel-yellow { + border-color: #f0ad4e; +} +.panel-yellow > .panel-heading { + border-color: #f0ad4e; + color: white; + background-color: #f0ad4e; +} +.panel-yellow > a { + color: #f0ad4e; +} +.panel-yellow > a:hover { + color: #df8a13; +} +.timeline { + position: relative; + padding: 20px 0 20px; + list-style: none; +} +.timeline:before { + content: " "; + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 3px; + margin-left: -1.5px; + background-color: #eeeeee; +} +.timeline > li { + position: relative; + margin-bottom: 20px; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li:before, +.timeline > li:after { + content: " "; + display: table; +} +.timeline > li:after { + clear: both; +} +.timeline > li > .timeline-panel { + float: left; + position: relative; + width: 46%; + padding: 20px; + border: 1px solid #d4d4d4; + border-radius: 2px; + -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); +} +.timeline > li > .timeline-panel:before { + content: " "; + display: inline-block; + position: absolute; + top: 26px; + right: -15px; + border-top: 15px solid transparent; + border-right: 0 solid #ccc; + border-bottom: 15px solid transparent; + border-left: 15px solid #ccc; +} +.timeline > li > .timeline-panel:after { + content: " "; + display: inline-block; + position: absolute; + top: 27px; + right: -14px; + border-top: 14px solid transparent; + border-right: 0 solid #fff; + border-bottom: 14px solid transparent; + border-left: 14px solid #fff; +} +.timeline > li > .timeline-badge { + z-index: 100; + position: absolute; + top: 16px; + left: 50%; + width: 50px; + height: 50px; + margin-left: -25px; + border-radius: 50% 50% 50% 50%; + text-align: center; + font-size: 1.4em; + line-height: 50px; + color: #fff; + background-color: #999999; +} +.timeline > li.timeline-inverted > .timeline-panel { + float: right; +} +.timeline > li.timeline-inverted > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} +.timeline > li.timeline-inverted > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} +.timeline-badge.primary { + background-color: #2e6da4 !important; +} +.timeline-badge.success { + background-color: #3f903f !important; +} +.timeline-badge.warning { + background-color: #f0ad4e !important; +} +.timeline-badge.danger { + background-color: #d9534f !important; +} +.timeline-badge.info { + background-color: #5bc0de !important; +} +.timeline-title { + margin-top: 0; + color: inherit; +} +.timeline-body > p, +.timeline-body > ul { + margin-bottom: 0; +} +.timeline-body > p + p { + margin-top: 5px; +} +@media (max-width: 767px) { + ul.timeline:before { + left: 40px; + } + ul.timeline > li > .timeline-panel { + width: calc(10%); + width: -moz-calc(10%); + width: -webkit-calc(10%); + } + ul.timeline > li > .timeline-badge { + top: 16px; + left: 15px; + margin-left: 0; + } + ul.timeline > li > .timeline-panel { + float: right; + } + ul.timeline > li > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; + } + ul.timeline > li > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; + } +} diff --git a/public/static/dist/css/sb-admin-2.min.css b/public/static/dist/css/sb-admin-2.min.css new file mode 100644 index 0000000..a685970 --- /dev/null +++ b/public/static/dist/css/sb-admin-2.min.css @@ -0,0 +1,5 @@ +/*! + * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2016 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) + */.chat,.timeline{list-style:none}body{background-color:#f8f8f8}#wrapper{width:100%}#page-wrapper{padding:0 15px;min-height:568px;background-color:#fff}@media (min-width:768px){#page-wrapper{position:inherit;margin:0 0 0 250px;padding:0 30px;border-left:1px solid #e7e7e7}}.navbar-top-links{margin-right:0}.navbar-top-links li{display:inline-block}.flot-chart,.navbar-top-links .dropdown-menu li{display:block}.navbar-top-links li:last-child{margin-right:15px}.navbar-top-links li a{padding:15px;min-height:50px}.navbar-top-links .dropdown-menu li:last-child{margin-right:0}.navbar-top-links .dropdown-menu li a{padding:3px 20px;min-height:0}.navbar-top-links .dropdown-menu li a div{white-space:normal}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{width:310px;min-width:0}.navbar-top-links .dropdown-messages{margin-left:5px}.navbar-top-links .dropdown-tasks{margin-left:-59px}.navbar-top-links .dropdown-alerts{margin-left:-123px}.navbar-top-links .dropdown-user{right:0;left:auto}.sidebar .sidebar-nav.navbar-collapse{padding-left:0;padding-right:0}.sidebar .sidebar-search{padding:15px}.sidebar ul li{border-bottom:1px solid #e7e7e7}.sidebar ul li a.active{background-color:#eee}.sidebar .arrow{float:right}.sidebar .fa.arrow:before{content:"\f104"}.sidebar .active>a>.fa.arrow:before{content:"\f107"}.sidebar .nav-second-level li,.sidebar .nav-third-level li{border-bottom:none!important}.sidebar .nav-second-level li a{padding-left:37px}.sidebar .nav-third-level li a{padding-left:52px}@media (min-width:768px){.sidebar{z-index:1;position:absolute;width:250px;margin-top:51px}.navbar-top-links .dropdown-alerts,.navbar-top-links .dropdown-messages,.navbar-top-links .dropdown-tasks{margin-left:auto}}.btn-outline{color:inherit;background-color:transparent;transition:all .5s}.btn-primary.btn-outline{color:#428bca}.btn-success.btn-outline{color:#5cb85c}.btn-info.btn-outline{color:#5bc0de}.btn-warning.btn-outline{color:#f0ad4e}.btn-danger.btn-outline{color:#d9534f}.btn-danger.btn-outline:hover,.btn-info.btn-outline:hover,.btn-primary.btn-outline:hover,.btn-success.btn-outline:hover,.btn-warning.btn-outline:hover{color:#fff}.chat{margin:0;padding:0}.chat li{margin-bottom:10px;padding-bottom:5px;border-bottom:1px dotted #999}.chat li.left .chat-body{margin-left:60px}.chat li.right .chat-body{margin-right:60px}.chat li .chat-body p{margin:0}.chat .glyphicon,.panel .slidedown .glyphicon{margin-right:5px}.chat-panel .panel-body{height:350px;overflow-y:scroll}.login-panel{margin-top:25%}.flot-chart{height:400px}.flot-chart-content{width:100%;height:100%}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{background:0 0}table.dataTable thead .sorting_asc:after{content:"\f0de";float:right;font-family:fontawesome}table.dataTable thead .sorting_desc:after{content:"\f0dd";float:right;font-family:fontawesome}table.dataTable thead .sorting:after{content:"\f0dc";float:right;font-family:fontawesome;color:rgba(50,50,50,.5)}.btn-circle{width:30px;height:30px;padding:6px 0;border-radius:15px;text-align:center;font-size:12px;line-height:1.428571429}.btn-circle.btn-lg{width:50px;height:50px;padding:10px 16px;border-radius:25px;font-size:18px;line-height:1.33}.btn-circle.btn-xl{width:70px;height:70px;padding:10px 16px;border-radius:35px;font-size:24px;line-height:1.33}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;border:1px solid #ddd;background-color:#eee!important}.show-grid{margin:15px 0}.huge{font-size:40px}.panel-green{border-color:#5cb85c}.panel-green>.panel-heading{border-color:#5cb85c;color:#fff;background-color:#5cb85c}.panel-green>a{color:#5cb85c}.panel-green>a:hover{color:#3d8b3d}.panel-red{border-color:#d9534f}.panel-red>.panel-heading{border-color:#d9534f;color:#fff;background-color:#d9534f}.panel-red>a{color:#d9534f}.panel-red>a:hover{color:#b52b27}.panel-yellow{border-color:#f0ad4e}.panel-yellow>.panel-heading{border-color:#f0ad4e;color:#fff;background-color:#f0ad4e}.panel-yellow>a{color:#f0ad4e}.panel-yellow>a:hover{color:#df8a13}.timeline{position:relative;padding:20px 0}.timeline:before{content:" ";position:absolute;top:0;bottom:0;left:50%;width:3px;margin-left:-1.5px;background-color:#eee}.timeline>li{position:relative;margin-bottom:20px}.timeline>li:after,.timeline>li:before{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-panel{float:left;position:relative;width:46%;padding:20px;border:1px solid #d4d4d4;border-radius:2px;-webkit-box-shadow:0 1px 6px rgba(0,0,0,.175);box-shadow:0 1px 6px rgba(0,0,0,.175)}.timeline>li>.timeline-panel:before{content:" ";display:inline-block;position:absolute;top:26px;right:-15px;border-top:15px solid transparent;border-right:0 solid #ccc;border-bottom:15px solid transparent;border-left:15px solid #ccc}.timeline>li>.timeline-panel:after{content:" ";display:inline-block;position:absolute;top:27px;right:-14px;border-top:14px solid transparent;border-right:0 solid #fff;border-bottom:14px solid transparent;border-left:14px solid #fff}.timeline>li>.timeline-badge{z-index:100;position:absolute;top:16px;left:50%;width:50px;height:50px;margin-left:-25px;border-radius:50%;text-align:center;font-size:1.4em;line-height:50px;color:#fff;background-color:#999}.timeline>li.timeline-inverted>.timeline-panel{float:right}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline-badge.primary{background-color:#2e6da4!important}.timeline-badge.success{background-color:#3f903f!important}.timeline-badge.warning{background-color:#f0ad4e!important}.timeline-badge.danger{background-color:#d9534f!important}.timeline-badge.info{background-color:#5bc0de!important}.timeline-title{margin-top:0;color:inherit}.timeline-body>p,.timeline-body>ul{margin-bottom:0}.timeline-body>p+p{margin-top:5px}@media (max-width:767px){ul.timeline:before{left:40px}ul.timeline>li>.timeline-panel{width:calc(10%);width:-moz-calc(10%);width:-webkit-calc(10%);float:right}ul.timeline>li>.timeline-badge{top:16px;left:15px;margin-left:0}ul.timeline>li>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}ul.timeline>li>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}} \ No newline at end of file diff --git a/public/static/dist/js/sb-admin-2.js b/public/static/dist/js/sb-admin-2.js new file mode 100644 index 0000000..24b26ce --- /dev/null +++ b/public/static/dist/js/sb-admin-2.js @@ -0,0 +1,47 @@ +/*! + * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2016 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) + */ +$(function() { + $('#side-menu').metisMenu(); +}); + +//Loads the correct sidebar on window load, +//collapses the sidebar on window resize. +// Sets the min-height of #page-wrapper to window size +$(function() { + $(window).bind("load resize", function() { + var topOffset = 50; + var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; + if (width < 768) { + $('div.navbar-collapse').addClass('collapse'); + topOffset = 100; // 2-row-menu + } else { + $('div.navbar-collapse').removeClass('collapse'); + } + + var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1; + height = height - topOffset; + if (height < 1) height = 1; + if (height > topOffset) { + $("#page-wrapper").css("min-height", (height) + "px"); + } + }); + + var url = window.location; + // var element = $('ul.nav a').filter(function() { + // return this.href == url; + // }).addClass('active').parent().parent().addClass('in').parent(); + var element = $('ul.nav a').filter(function() { + return this.href == url; + }).addClass('active').parent(); + + while (true) { + if (element.is('li')) { + element = element.parent().addClass('in').parent(); + } else { + break; + } + } +}); diff --git a/public/static/dist/js/sb-admin-2.min.js b/public/static/dist/js/sb-admin-2.min.js new file mode 100644 index 0000000..892ea1e --- /dev/null +++ b/public/static/dist/js/sb-admin-2.min.js @@ -0,0 +1,6 @@ +/*! + * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2016 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) + */ +$(function(){$("#side-menu").metisMenu()}),$(function(){$(window).bind("load resize",function(){var i=50,n=this.window.innerWidth>0?this.window.innerWidth:this.screen.width;n<768?($("div.navbar-collapse").addClass("collapse"),i=100):$("div.navbar-collapse").removeClass("collapse");var e=(this.window.innerHeight>0?this.window.innerHeight:this.screen.height)-1;e-=i,e<1&&(e=1),e>i&&$("#page-wrapper").css("min-height",e+"px")});for(var i=window.location,n=$("ul.nav a").filter(function(){return this.href==i}).addClass("active").parent();;){if(!n.is("li"))break;n=n.parent().addClass("in").parent()}}); \ No newline at end of file diff --git a/public/static/fanfic_13.png b/public/static/fanfic_13.png new file mode 100644 index 0000000..ae9887a Binary files /dev/null and b/public/static/fanfic_13.png differ diff --git a/public/static/index/images/loading-logo.png b/public/static/index/images/loading-logo.png new file mode 100644 index 0000000..07831fc Binary files /dev/null and b/public/static/index/images/loading-logo.png differ diff --git a/public/static/index/images/slide1.jpg b/public/static/index/images/slide1.jpg new file mode 100644 index 0000000..36cd349 Binary files /dev/null and b/public/static/index/images/slide1.jpg differ diff --git a/public/static/index/script/bootstrap.min.js b/public/static/index/script/bootstrap.min.js new file mode 100644 index 0000000..c6d3692 --- /dev/null +++ b/public/static/index/script/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.2 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.2",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.2",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('